Skip to content

Commit

Permalink
Reading syscall arguments from user program's stack
Browse files Browse the repository at this point in the history
  • Loading branch information
taikiy committed Dec 31, 2023
1 parent f65341e commit c0c471c
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 38 deletions.
39 changes: 39 additions & 0 deletions doc/13_calling_kernel_space_routines_from_user_space.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,42 @@ print:
- Interrupt 0x80 handler [commit](https://github.com/taikiy/kernel/commit/42c0b6374e21e096060d27e3255a2e007c55b0cd)
- Register 0x80 handler in IDT [commit](https://github.com/taikiy/kernel/commit/2020d58d9047f2584ab03d95ccfab2b221ff2ced)
- `sys_print` function [commit](https://github.com/taikiy/kernel/commit/459b80a2d26d9ff61d42f026305b79abbe5acf4f)

## Passing arguments

We can pass arguments from the user space programs to the kernel space (syscall) by pushing them to the stack before making `int 0x80` call. The kernel can then read the arguments from the task's stack.

`get_arg_from_task() @ syscall.c:18`

---

`--- User Space ---`

1. The user program pushes the arguments to the stack.
2. The user program sets the kernel command ID (`SYSCALL_COMMAND` in our source) to EAX.
3. The user program executes the `int 0x80` instruction.

`--- Kernel Space ---`

4. The kernel saves the user program's registers to the task instance.
5. The kernel calls the syscall handler.
6. The syscall handler reads the saved registers from the task instance. ESP points to the top of the stack.
7. The syscall handler switches to the User Space to read the arguments from the stack.

`--- User Space ---`

8. For each argument, the syscall reads the value at `$esp + index`. The value is `uint32_t` which is the address of the argument value at `index`. Then, we cast it to `uint32_t*` and dereference it to get the value.
9. The syscall handler switches to the Kernel Space.

`--- Kernel Space ---`

10. The syscall casts the value to `void*` because the value could be of any type. The caller is responsible for casting the value to the correct type. If it's an address to some data (string, struct, etc.), the caller must call `copy_data_from_user_space()` to copy the user space data to the kernel space.

---

- Implement `copy_data_from_user_space()` [commit](https://github.com/taikiy/kernel/commit/ff3f410d753d25828b2af2442c19f19d95245d29)
- Reading the task stack (syscall arguments) [commit]()

---

[Previous](./12_user_space.md) | [Next](./14_accessing_keyboard_in_protected_mode.md) | [Home](../README.md)
24 changes: 19 additions & 5 deletions programs/syscall/src/syscall.asm
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
[BITS 32]

section .asm
section .asm

global _start
global _start

_start:
mov eax, 0 ; sys_print
int 0x80 ; call kernel
jmp $
; arguments to sys_sum
push 20
push 30

mov eax, 0 ; sys_sum
int 0x80 ; call kernel
add esp, 8 ; clean up stack

push message
mov eax, 1
int 0x80
add esp, 4

jmp $

section .data
message: db "Hello, World!", 0
2 changes: 1 addition & 1 deletion src/disk/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ disk_stream_read(struct disk_stream* stream, char* buf, unsigned int size)
for (int i = 0; i < size; i++) {
if (stream->offset == 0 || i == 0) {
result = disk_read_block(stream->disk, stream->sector, 1, block);
if (result < 0) {
if (result != ALL_OK) {
goto out;
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ test_syscall()
if (result != ALL_OK || !proc) {
panic("Failed to create a process!");
}
print("Executing 0:/syscall.bin\n");
start_tasks();
}

Expand All @@ -81,7 +82,7 @@ test_user_space()
if (result != ALL_OK || !proc) {
panic("Failed to create a process!");
}
print("Process created successfully!\n");
print("Executing 0:/blank.bin\n");
start_tasks();
}

Expand Down
5 changes: 3 additions & 2 deletions src/memory/heap/kheap.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "heap.h"
#include "memory/memory.h"
#include "status.h"
#include "system/sys.h"
#include "terminal/terminal.h"

struct heap kernel_heap;
Expand All @@ -17,8 +18,8 @@ initialize_kernel_heap()
void* end = (void*)HEAP_ADDRESS + HEAP_SIZE_BYTES;
status_t result = heap_create(&kernel_heap, (void*)HEAP_ADDRESS, end, &kernel_heap_table);

if (result < 0) {
print("Failed to create heap\n");
if (result != ALL_OK) {
panic("Failed to create heap\n");
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/memory/paging/paging.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ set_table_entry(struct paging_map* map, void* virtual_address, uint32_t table_en
uint32_t directory_index = 0;
uint32_t table_index = 0;
status_t result = get_page_indexes(virtual_address, &directory_index, &table_index);
if (result < 0) {
if (result != ALL_OK) {
return result;
}

Expand All @@ -90,7 +90,7 @@ get_table_entry(struct paging_map* map, void* virtual_address, uint32_t* table_e
uint32_t directory_index = 0;
uint32_t table_index = 0;
status_t result = get_page_indexes(virtual_address, &directory_index, &table_index);
if (result < 0) {
if (result != ALL_OK) {
return ERROR(EPAGEFAULT);
}

Expand Down Expand Up @@ -194,7 +194,7 @@ map_physical_address_to_pages(
for (uint32_t i = 0; i < total_pages; i++) {
uint32_t table_entry = (uint32_t)physical_address | flags;
result = set_table_entry(map, virtual_address, table_entry);
if (result < 0) {
if (result != ALL_OK) {
goto out;
}
physical_address += PAGING_PAGE_SIZE_BYTES;
Expand Down
57 changes: 56 additions & 1 deletion src/system/syscall.c
Original file line number Diff line number Diff line change
@@ -1,14 +1,68 @@
#include "syscall.h"
#include "config.h"
#include "memory/heap/kheap.h"
#include "memory/paging/paging.h"
#include "sys.h"
#include "task/task.h"
#include "terminal/terminal.h"

static SYSCALL_HANDLER syscall_handlers[TOTAL_SYSCALL_COUNT];

/// @brief Returns an argument value at the given index passed to the syscall. This function returns a void pointer
/// because the argument can be of any type. The caller is responsible for casting the result to the correct type or
/// use `copy_data_from_user_space()` to copy the data pointed by the argument value.
/// @param task The task making the syscall.
/// @param index The index of the argument.
/// @return The argument value.
static void*
get_arg_from_task(struct task* task, int index)
{
switch_to_user_page();
// `$esp + index` is `uint32_t`. This is the address of the argument value at `index`.
// Then, we cast it to `uint32_t*` and dereference it to get the value.
uint32_t arg = *(uint32_t*)(task->registers.esp + (index * sizeof(uint32_t)));
switch_to_kernel_page();

return (void*)arg;
}

void*
sys_sum(struct interrupt_frame* frame)
{
print("sys_sum\n");

struct task* current_task = get_current_task();

// `sys_sum` takes two `int` arguments. Since `int` is a 4-byte primitive type, the value is directly stored in
// the stack. We can get the value by simply casting the argument value to `int`.
int arg1 = (int)get_arg_from_task(current_task, 0);
int arg2 = (int)get_arg_from_task(current_task, 1);

print("arg1: ");
print_int(arg1);
print("\narg2: ");
print_int(arg2);
print("\n");

return (void*)arg1 + arg2;
}

void*
sys_print(struct interrupt_frame* frame)
{
print("sys_print\n");

struct task* current_task = get_current_task();

// `sys_print` takes one `char*` argument. Since `char*` is a pointer type, the value is the address of the string
// in the user space. We can get the string from the address by using `copy_data_from_user_space()`.
char* arg1 = (char*)get_arg_from_task(current_task, 0);
char buf[4096];
copy_data_from_user_space(current_task, arg1, &buf, sizeof(buf));

print(buf);
print("\n");

return 0;
}

Expand All @@ -29,7 +83,8 @@ register_syscall_handler(int command, SYSCALL_HANDLER handler)
void
initialize_syscall_handlers()
{
register_syscall_handler(SYSCALL_COMMAND_0_PRINT, sys_print);
register_syscall_handler(SYSCALL_COMMAND_0_SUM, sys_sum);
register_syscall_handler(SYSCALL_COMMAND_1_PRINT, sys_print);
}

void*
Expand Down
3 changes: 2 additions & 1 deletion src/system/syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ typedef void* (*SYSCALL_HANDLER)(struct interrupt_frame*);

enum SYSCALL_COMMAND
{
SYSCALL_COMMAND_0_PRINT = 0,
SYSCALL_COMMAND_0_SUM = 0,
SYSCALL_COMMAND_1_PRINT = 1,
};

void initialize_syscall_handlers();
Expand Down
51 changes: 27 additions & 24 deletions src/task/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,42 +139,45 @@ free_process(struct process* process)
status_t
map_binary_data(struct process* process)
{
status_t result = ALL_OK;

if (!process) {
return ERROR(EINVARG);
}

if (!process->data) {
return ERROR(EINVARG);
}

if (!process->task) {
return ERROR(EINVARG);
}

struct paging_map* chunk = process->task->user_page;

if (!chunk) {
return ERROR(EINVARG);
}

map_physical_address_to_pages(
chunk,
return map_physical_address_to_pages(
process->task->user_page,
process->data,
(void*)USER_PROGRAM_VIRTUAL_ADDRESS_START,
process->size,
PAGING_IS_PRESENT | PAGING_IS_WRITABLE | PAGING_ACCESS_FROM_ALL
);
}

return result;
status_t
map_stack(struct process* process)
{
return map_physical_address_to_pages(
process->task->user_page,
process->stack,
(void*)USER_PROGRAM_STACK_VIRTUAL_ADDRESS_END, // END because the stack grows downwards
USER_PROGRAM_STACK_SIZE,
PAGING_IS_PRESENT | PAGING_IS_WRITABLE | PAGING_ACCESS_FROM_ALL
);
}

status_t
map_process_memory(struct process* process)
{
status_t result = ALL_OK;

result = map_stack(process);
if (result != ALL_OK) {
goto out;
}

// TODO: implement for different file types
return map_binary_data(process);
result = map_binary_data(process);
if (result != ALL_OK) {
goto out;
}

out:
return result;
}

/// @brief Loads data from `file_path`, creates a new process and saves it at `slot` of `processes`.
Expand Down

0 comments on commit c0c471c

Please sign in to comment.