2020-07-18 22:46:24 +01:00
|
|
|
const std = @import("std");
|
|
|
|
const expectEqual = std.testing.expectEqual;
|
|
|
|
const expectError = std.testing.expectError;
|
|
|
|
const builtin = @import("builtin");
|
|
|
|
const is_test = builtin.is_test;
|
|
|
|
const build_options = @import("build_options");
|
|
|
|
const mock_path = build_options.mock_path;
|
|
|
|
const arch = @import("arch.zig").internals;
|
|
|
|
const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("panic.zig").panic;
|
|
|
|
const ComptimeBitmap = @import("bitmap.zig").ComptimeBitmap;
|
2020-07-24 00:18:56 +01:00
|
|
|
const vmm = @import("vmm.zig");
|
2020-07-18 22:46:24 +01:00
|
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
|
|
|
|
/// The kernels main stack start as this is used to check for if the task being destroyed is this stack
|
|
|
|
/// as we cannot deallocate this.
|
|
|
|
extern var KERNEL_STACK_START: *u32;
|
|
|
|
|
|
|
|
/// The function type for the entry point.
|
2020-07-24 00:18:56 +01:00
|
|
|
pub const EntryPoint = usize;
|
2020-07-18 22:46:24 +01:00
|
|
|
|
|
|
|
/// The bitmap type for the PIDs
|
|
|
|
const PidBitmap = if (is_test) ComptimeBitmap(u128) else ComptimeBitmap(u1024);
|
|
|
|
|
|
|
|
/// The list of PIDs that have been allocated.
|
|
|
|
var all_pids: PidBitmap = brk: {
|
|
|
|
var pids = PidBitmap.init();
|
|
|
|
// Set the first PID as this is for the current task running, init 0
|
|
|
|
_ = pids.setFirstFree() orelse unreachable;
|
|
|
|
break :brk pids;
|
|
|
|
};
|
|
|
|
|
2020-07-24 00:18:56 +01:00
|
|
|
/// The default stack size of a task. Currently this is set to a page size.
|
|
|
|
pub const STACK_SIZE: u32 = arch.MEMORY_BLOCK_SIZE / @sizeOf(u32);
|
|
|
|
|
2020-07-18 22:46:24 +01:00
|
|
|
/// The task control block for storing all the information needed to save and restore a task.
|
|
|
|
pub const Task = struct {
|
|
|
|
const Self = @This();
|
|
|
|
|
|
|
|
/// The unique task identifier
|
|
|
|
pid: PidBitmap.IndexType,
|
|
|
|
|
2020-07-24 00:18:56 +01:00
|
|
|
/// Pointer to the kernel stack for the task. This will be allocated on initialisation.
|
|
|
|
kernel_stack: []usize,
|
|
|
|
|
|
|
|
/// Pointer to the user stack for the task. This will be allocated on initialisation and will be empty if it's a kernel task
|
|
|
|
user_stack: []usize,
|
2020-07-18 22:46:24 +01:00
|
|
|
|
|
|
|
/// The current stack pointer into the stack.
|
|
|
|
stack_pointer: usize,
|
|
|
|
|
2020-07-24 00:18:56 +01:00
|
|
|
/// Whether the process is a kernel process or not
|
|
|
|
kernel: bool,
|
|
|
|
|
|
|
|
/// The virtual memory manager belonging to the task
|
|
|
|
vmm: *vmm.VirtualMemoryManager(arch.VmmPayload),
|
|
|
|
|
2020-07-18 22:46:24 +01:00
|
|
|
///
|
|
|
|
/// Create a task. This will allocate a PID and the stack. The stack will be set up as a
|
|
|
|
/// kernel task. As this is a new task, the stack will need to be initialised with the CPU
|
|
|
|
/// state as described in arch.CpuState struct.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
2020-07-24 00:18:56 +01:00
|
|
|
/// IN entry_point: EntryPoint - The entry point into the task. This must be a function.
|
|
|
|
/// IN kernel: bool - Whether the task has kernel or user privileges.
|
|
|
|
/// IN task_vmm: *VirtualMemoryManager - The virtual memory manager associated with the task.
|
2020-07-18 22:46:24 +01:00
|
|
|
/// IN allocator: *Allocator - The allocator for allocating memory for a task.
|
|
|
|
///
|
|
|
|
/// Return: *Task
|
|
|
|
/// Pointer to an allocated task. This will then need to be added to the task queue.
|
|
|
|
///
|
|
|
|
/// Error: Allocator.Error
|
|
|
|
/// OutOfMemory - If there is no more memory to allocate. Any memory or PID allocated will
|
|
|
|
/// be freed on return.
|
|
|
|
///
|
2020-07-24 00:18:56 +01:00
|
|
|
pub fn create(entry_point: EntryPoint, kernel: bool, task_vmm: *vmm.VirtualMemoryManager(arch.VmmPayload), allocator: *Allocator) Allocator.Error!*Task {
|
2020-07-18 22:46:24 +01:00
|
|
|
var task = try allocator.create(Task);
|
|
|
|
errdefer allocator.destroy(task);
|
|
|
|
|
2020-07-24 00:18:56 +01:00
|
|
|
const pid = allocatePid();
|
|
|
|
errdefer freePid(pid);
|
2020-07-18 22:46:24 +01:00
|
|
|
|
2020-07-24 00:18:56 +01:00
|
|
|
var k_stack = try allocator.alloc(usize, STACK_SIZE);
|
|
|
|
errdefer allocator.free(k_stack);
|
|
|
|
|
|
|
|
var u_stack = if (kernel) &[_]usize{} else try allocator.alloc(usize, STACK_SIZE);
|
|
|
|
errdefer if (!kernel) allocator.free(u_stack);
|
|
|
|
|
|
|
|
task.* = .{
|
|
|
|
.pid = pid,
|
|
|
|
.kernel_stack = k_stack,
|
|
|
|
.user_stack = u_stack,
|
|
|
|
.stack_pointer = @ptrToInt(&k_stack[STACK_SIZE - 1]),
|
|
|
|
.kernel = kernel,
|
|
|
|
.vmm = task_vmm,
|
|
|
|
};
|
|
|
|
|
|
|
|
try arch.initTask(task, entry_point, allocator);
|
2020-07-18 22:46:24 +01:00
|
|
|
|
|
|
|
return task;
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Destroy the task. This will release the allocated PID and free the stack and self.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// IN/OUT self: *Self - The pointer to self.
|
|
|
|
///
|
|
|
|
pub fn destroy(self: *Self, allocator: *Allocator) void {
|
|
|
|
freePid(self.pid);
|
|
|
|
// We need to check that the the stack has been allocated as task 0 (init) won't have a
|
|
|
|
// stack allocated as this in the linker script
|
2020-07-24 00:18:56 +01:00
|
|
|
if (@ptrToInt(self.kernel_stack.ptr) != @ptrToInt(&KERNEL_STACK_START)) {
|
|
|
|
allocator.free(self.kernel_stack);
|
|
|
|
}
|
|
|
|
if (!self.kernel) {
|
|
|
|
allocator.free(self.user_stack);
|
2020-07-18 22:46:24 +01:00
|
|
|
}
|
|
|
|
allocator.destroy(self);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Allocate a process identifier. If out of PIDs, then will panic. Is this occurs, will need to
|
|
|
|
/// increase the bitmap.
|
|
|
|
///
|
|
|
|
/// Return: u32
|
|
|
|
/// A new PID.
|
|
|
|
///
|
|
|
|
fn allocatePid() PidBitmap.IndexType {
|
|
|
|
return all_pids.setFirstFree() orelse panic(@errorReturnTrace(), "Out of PIDs\n", .{});
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Free an allocated PID. One must be allocated to be freed. If one wasn't allocated will panic.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// IN pid: u32 - The PID to free.
|
|
|
|
///
|
|
|
|
fn freePid(pid: PidBitmap.IndexType) void {
|
|
|
|
if (!all_pids.isSet(pid)) {
|
|
|
|
panic(@errorReturnTrace(), "PID {} not allocated\n", .{pid});
|
|
|
|
}
|
|
|
|
all_pids.clearEntry(pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
// For testing the errdefer
|
|
|
|
const FailingAllocator = std.testing.FailingAllocator;
|
|
|
|
const testing_allocator = &std.testing.base_allocator_instance.allocator;
|
|
|
|
|
|
|
|
fn test_fn1() void {}
|
|
|
|
|
|
|
|
test "create out of memory for task" {
|
|
|
|
// Set the global allocator
|
|
|
|
var fa = FailingAllocator.init(testing_allocator, 0);
|
|
|
|
|
2020-07-24 00:18:56 +01:00
|
|
|
expectError(error.OutOfMemory, Task.create(@ptrToInt(test_fn1), true, undefined, &fa.allocator));
|
|
|
|
expectError(error.OutOfMemory, Task.create(@ptrToInt(test_fn1), false, undefined, &fa.allocator));
|
2020-07-18 22:46:24 +01:00
|
|
|
|
|
|
|
// Make sure any memory allocated is freed
|
|
|
|
expectEqual(fa.allocated_bytes, fa.freed_bytes);
|
|
|
|
|
|
|
|
// Make sure no PIDs were allocated
|
|
|
|
expectEqual(all_pids.bitmap, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
test "create out of memory for stack" {
|
|
|
|
// Set the global allocator
|
|
|
|
var fa = FailingAllocator.init(testing_allocator, 1);
|
|
|
|
|
2020-07-24 00:18:56 +01:00
|
|
|
expectError(error.OutOfMemory, Task.create(@ptrToInt(test_fn1), true, undefined, &fa.allocator));
|
|
|
|
expectError(error.OutOfMemory, Task.create(@ptrToInt(test_fn1), false, undefined, &fa.allocator));
|
2020-07-18 22:46:24 +01:00
|
|
|
|
|
|
|
// Make sure any memory allocated is freed
|
|
|
|
expectEqual(fa.allocated_bytes, fa.freed_bytes);
|
|
|
|
|
|
|
|
// Make sure no PIDs were allocated
|
|
|
|
expectEqual(all_pids.bitmap, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
test "create expected setup" {
|
2020-07-24 00:18:56 +01:00
|
|
|
var task = try Task.create(@ptrToInt(test_fn1), true, undefined, std.testing.allocator);
|
2020-07-18 22:46:24 +01:00
|
|
|
defer task.destroy(std.testing.allocator);
|
|
|
|
|
|
|
|
// Will allocate the first PID 1, 0 will always be allocated
|
|
|
|
expectEqual(task.pid, 1);
|
2020-07-24 00:18:56 +01:00
|
|
|
expectEqual(task.kernel_stack.len, STACK_SIZE);
|
|
|
|
expectEqual(task.user_stack.len, 0);
|
|
|
|
|
|
|
|
var user_task = try Task.create(@ptrToInt(test_fn1), false, undefined, std.testing.allocator);
|
|
|
|
defer user_task.destroy(std.testing.allocator);
|
|
|
|
expectEqual(user_task.pid, 2);
|
|
|
|
expectEqual(user_task.user_stack.len, STACK_SIZE);
|
|
|
|
expectEqual(user_task.kernel_stack.len, STACK_SIZE);
|
2020-07-18 22:46:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
test "destroy cleans up" {
|
|
|
|
// This used the leak detector allocator in testing
|
|
|
|
// So if any alloc were not freed, this will fail the test
|
2020-07-24 00:18:56 +01:00
|
|
|
var allocator = std.testing.allocator;
|
2020-07-18 22:46:24 +01:00
|
|
|
|
2020-07-24 00:18:56 +01:00
|
|
|
var task = try Task.create(@ptrToInt(test_fn1), true, undefined, allocator);
|
|
|
|
var user_task = try Task.create(@ptrToInt(test_fn1), false, undefined, allocator);
|
2020-07-18 22:46:24 +01:00
|
|
|
|
2020-07-24 00:18:56 +01:00
|
|
|
task.destroy(allocator);
|
|
|
|
user_task.destroy(allocator);
|
2020-07-18 22:46:24 +01:00
|
|
|
|
|
|
|
// All PIDs were freed
|
|
|
|
expectEqual(all_pids.bitmap, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
test "Multiple create" {
|
2020-07-24 00:18:56 +01:00
|
|
|
var task1 = try Task.create(@ptrToInt(test_fn1), true, undefined, std.testing.allocator);
|
|
|
|
var task2 = try Task.create(@ptrToInt(test_fn1), true, undefined, std.testing.allocator);
|
2020-07-18 22:46:24 +01:00
|
|
|
|
|
|
|
expectEqual(task1.pid, 1);
|
|
|
|
expectEqual(task2.pid, 2);
|
|
|
|
expectEqual(all_pids.bitmap, 7);
|
|
|
|
|
|
|
|
task1.destroy(std.testing.allocator);
|
|
|
|
|
|
|
|
expectEqual(all_pids.bitmap, 5);
|
|
|
|
|
2020-07-24 00:18:56 +01:00
|
|
|
var task3 = try Task.create(@ptrToInt(test_fn1), true, undefined, std.testing.allocator);
|
2020-07-18 22:46:24 +01:00
|
|
|
|
|
|
|
expectEqual(task3.pid, 1);
|
|
|
|
expectEqual(all_pids.bitmap, 7);
|
|
|
|
|
|
|
|
task2.destroy(std.testing.allocator);
|
|
|
|
task3.destroy(std.testing.allocator);
|
2020-07-24 00:18:56 +01:00
|
|
|
|
|
|
|
var user_task = try Task.create(@ptrToInt(test_fn1), false, undefined, std.testing.allocator);
|
|
|
|
|
|
|
|
expectEqual(user_task.pid, 1);
|
|
|
|
expectEqual(all_pids.bitmap, 3);
|
|
|
|
|
|
|
|
user_task.destroy(std.testing.allocator);
|
|
|
|
expectEqual(all_pids.bitmap, 1);
|
2020-07-18 22:46:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
test "allocatePid and freePid" {
|
|
|
|
expectEqual(all_pids.bitmap, 1);
|
|
|
|
|
|
|
|
var i: usize = 1;
|
|
|
|
while (i < PidBitmap.NUM_ENTRIES) : (i += 1) {
|
|
|
|
expectEqual(i, allocatePid());
|
|
|
|
}
|
|
|
|
|
|
|
|
expectEqual(all_pids.bitmap, PidBitmap.BITMAP_FULL);
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while (i < PidBitmap.NUM_ENTRIES) : (i += 1) {
|
|
|
|
freePid(@truncate(PidBitmap.IndexType, i));
|
|
|
|
}
|
|
|
|
|
|
|
|
expectEqual(all_pids.bitmap, 0);
|
|
|
|
}
|