Basic userspace driver infrastructure

This commit is contained in:
Eric-Paul Ickhorn 2023-05-29 15:42:56 +02:00
parent 6ddd4587cf
commit 2f9ca93cb1
16 changed files with 642 additions and 74 deletions

View File

@ -82,16 +82,18 @@ typedef struct {
json_node_T* root_node;
} json_T;
json_T* json_from_string (string_t str);
void json_destruct (json_T* json);
json_node_T* json_node_alloc (json_node_T* parent, json_node_type_E type, json_token_T* token);
void json_node_destruct (json_node_T* node);
void json_node_dump (json_node_T* node, uint32_t indent);
string_t json_node_type_to_string (json_node_type_E type);
void json_tokenize (json_T* json, string_t str);
bool json_parse (json_T* json);
bool json_parse_assignment (json_T* json, uint32_t* token_id, json_node_T* node);
bool json_parse_object (json_T* json, uint32_t* token_id, json_node_T* node);
bool json_parse_array (json_T* json, uint32_t* token_id, json_node_T* node);
json_T * json_from_string (string_t str);
void json_destruct (json_T* json);
json_node_T * json_node_alloc (json_node_T* parent, json_node_type_E type, json_token_T* token);
void json_node_destruct (json_node_T* node);
void json_node_dump (json_node_T* node, uint32_t indent);
string_t json_node_type_to_string (json_node_type_E type);
void json_tokenize (json_T* json, string_t str);
bool json_parse (json_T* json);
bool json_parse_assignment (json_T* json, uint32_t* token_id, json_node_T* node);
bool json_parse_object (json_T* json, uint32_t* token_id, json_node_T* node);
bool json_parse_array (json_T* json, uint32_t* token_id, json_node_T* node);
json_node_T * json_resolve_path (json_node_T *base, char *path);
#endif //NOXOS_JSON_H

View File

@ -0,0 +1,79 @@
#ifndef NOXOS_DEVICE_H
#define NOXOS_DEVICE_H
#include <utils/stdtypes.h>
#include <drivers/driver_manager.h>
typedef enum {
DEVICE_PCI = 1,
DEVICE_USB = 2,
DEVICE_VIRTUAL = 255
} device_category_E;
typedef enum {
DEVICE_CAPABILITY_TRANSMISSION,
DEVICE_CAPABILITY_STORAGE,
DEVICE_CAPABILITY_GRAPHICS,
DEVICE_CAPABILITY_AUDIO
} device_top_level_capability_E;
typedef struct device_capabilities_t {
device_top_level_capability_E *top_level;
struct device_transmission_capability_t {
// TODO
} transmission;
struct device_storage_capability_t {
char uuid[32];
uint64_t size;
} storage;
struct device_graphics_capability_t {
uint32_t fb_width;
uint32_t fb_height;
uint16_t bits_per_pixel;
} graphics;
struct device_audio_capability_t {
// TODO
} audio;
} device_capabilities_T;
typedef struct device_t {
uint32_t id;
device_category_E category;
void *specific;
driver_T *driver;
} device_T;
typedef struct device_manager_t {
uint32_t num_devices;
device_T *devices;
} device_manager_T;
extern device_manager_T g_device_manager;
void device_manager_init ();
void device_remove (device_T *device);
device_T * device_find (device_capabilities_T capabilities, uint32_t index, uint32_t *out_count);
#endif

View File

@ -0,0 +1,117 @@
#ifndef NOXOS_DRIVER_H
#define NOXOS_DRIVER_H
#include "utils/stdtypes.h"
#define DRIVER_COMMAND_SIZE 32
typedef enum {
DRIVER_UNKNOWN = 0x0000,
DRIVER_RESERVED = 0x0001,
// Specific Driver
DRIVER_MASS_STORAGE = 0x0101,
DRIVER_FILESYSTEM = 0x0102,
DRIVER_GRAPHICS = 0x0103,
DRIVER_AUDIO = 0x0104,
// Drivers which define the transmission of data but have multiple
// intended usages, such as USB for mass storage, input, etc..
DRIVER_TRANSMISSION = 0x0201,
DRIVER_USB_DEVICE = 0x0202,
DRIVER_PCI_DEVICE = 0x0203,
DRIVER_PCIE_DEVICE = 0x0204,
DRIVER_NVME_DEVICE = 0x0205,
DRIVER_BLUETOOTH = 0x0206,
DRIVER_ETHERNET = 0x0207,
// MISCELLANEOUS
DRIVER_EMULATION = 0x7fff
} driver_category_E;
typedef struct driver_mass_storage_t {
} driver_mass_storage_T;
typedef struct driver_configuration_t
{
uint16_t category;
uint16_t max_command_buffers;
uint16_t category_dependencies[16];
uint8_t reserved[220];
} driver_configuration_T;
typedef struct driver_command_buffer_config_t
{
uint64_t num_commands;
void *mapping_address;
uint8_t reserved[240];
} driver_command_buffer_config_T;
typedef struct driver_command_buffer_t
{
uint64_t command_capacity;
void *mapping_address;
} driver_command_buffer_T;
typedef struct driver_t // TODO: Accesses must be thread-safe
{
driver_category_E category;
uint32_t id;
uint16_t max_command_buffers;
driver_command_buffer_T *command_buffers;
} driver_T;
typedef struct driver_manager_t {
uint32_t max_drivers;
uint32_t num_drivers;
driver_T *drivers;
} driver_manager_T;
extern driver_manager_T g_driver_manager;
void driver_manager_init ();
void driver_manager_load_single_driver (char *driver_config_path);
void driver_manager_load_drivers (char *main_config_path);
uint32_t driver_manager_register_driver (driver_configuration_T *configuration);
driver_T * driver_manager_resolve_driver_id (uint32_t id);
// driver_add_command_buffer: Adds a command buffer to the 'command_buffers'-array in the given driver.
void driver_add_command_buffer (driver_T *driver, driver_command_buffer_config_T *configuration);
// driver_flush_command_buffer: Flushes the command buffer that 'buffer' points to,
// meaning that it executes all commands stored in it. 'buffer' doesn't need to be the first
// byte; the function will execute from the first command in a buffer even if some byte in the
// middle of the buffer is given into this function.
void driver_flush_command_buffer (driver_T *driver, void *buffer);
#endif

View File

@ -7,9 +7,10 @@
#include "utils/status.h"
typedef enum {
SYSCALLS_FILES = 0x01,
SYSCALLS_MEMORY = 0x02,
SYSCALLS_PROC = 0x03,
SYSCALLS_FILES = 0x00,
SYSCALLS_MEMORY = 0x01,
SYSCALLS_PROCESS = 0x02,
SYSCALLS_DRIVER = 0x03,
SYSCALLS_RUNTIME_LINKER = 0x04,
SYSCALLS_COMPATABILITY = 0x05,
@ -17,40 +18,44 @@ typedef enum {
}syscall_group_E;
typedef enum {
SYSCALL_FILES_OPEN = 0x0101,
SYSCALL_FILES_CLOSE = 0x0102,
SYSCALL_FILES_READ = 0x0103,
SYSCALL_FILES_WRITE = 0x0104,
SYSCALL_FILES_DELETE = 0x0105,
SYSCALL_FILES_LIST = 0x0106,
SYSCALL_FILES_INFO = 0x0107,
SYSCALL_FILES_OPEN = 0x0001,
SYSCALL_FILES_CLOSE = 0x0002,
SYSCALL_FILES_READ = 0x0003,
SYSCALL_FILES_WRITE = 0x0004,
SYSCALL_FILES_DELETE = 0x0005,
SYSCALL_FILES_LIST = 0x0006,
SYSCALL_FILES_INFO = 0x0007,
SYSCALL_MEMORY_MAP = 0x0201,
SYSCALL_MEMORY_UNMAP = 0x0202,
SYSCALL_MEMORY_LABEL = 0x0203,
SYSCALL_MEMORY_RANGE = 0x0204,
SYSCALL_MEMORY_ACCESS = 0x0205,
SYSCALL_MEMORY_MAP = 0x0101,
SYSCALL_MEMORY_UNMAP = 0x0102,
SYSCALL_MEMORY_LABEL = 0x0103,
SYSCALL_MEMORY_RANGE = 0x0104,
SYSCALL_MEMORY_ACCESS = 0x0105,
SYSCALL_PROCESS_CREATE = 0x0301,
SYSCALL_PROCESS_ENV = 0x0302,
SYSCALL_PROCESS_SIGNAL = 0x0303,
SYSCALL_PROCESS_THREAD_CREATE = 0x0304,
SYSCALL_PROCESS_THREAD_START = 0x0305,
SYSCALL_PROCESS_THREAD_PAUSE = 0x0306,
SYSCALL_PROCESS_THREAD_KILL = 0x0307,
SYSCALL_PROCESS_CREATE = 0x0201,
SYSCALL_PROCESS_ENV = 0x0202,
SYSCALL_PROCESS_SIGNAL = 0x0203,
SYSCALL_PROCESS_THREAD_CREATE = 0x0204,
SYSCALL_PROCESS_THREAD_START = 0x0205,
SYSCALL_PROCESS_THREAD_PAUSE = 0x0206,
SYSCALL_PROCESS_THREAD_KILL = 0x0207,
SYSCALL_DRIVER_REGISTER = 0x0301,
SYSCALL_DRIVER_CREATE_COMMAND_BUFFER = 0x0302,
SYSCALL_DRIVER_FLUSH_COMMAND_BUFFER = 0x0303,
SYSCALL_RUNTIME_LINKER_OPEN = 0x7f01,
SYSCALL_RUNTIME_LINKER_CLOSE = 0x7f02,
SYSCALL_RUNTIME_LINKER_LOAD_SYMBOL = 0x7f03,
SYSCALL_RUNTIME_LINKER_STATUS = 0x7f04,
SYSCALL_RUNTIME_LINKER_STANDARD_MOD = 0x7f05,
SYSCALL_RUNTIME_LINKER_OPEN = 0x0401,
SYSCALL_RUNTIME_LINKER_CLOSE = 0x0402,
SYSCALL_RUNTIME_LINKER_LOAD_SYMBOL = 0x0403,
SYSCALL_RUNTIME_LINKER_STATUS = 0x0404,
SYSCALL_RUNTIME_LINKER_STANDARD_MOD = 0x0405,
SYSCALL_COMPATABILITY_ABI_TYPE = 0x0501,
SYSCALL_COMPATABILITY_ABI_VERSION = 0x0502,
SYSCALL_COMPATABILITY_ACTION = 0x0503,
SYSCALL_COMPATABILITY_ABI_TYPE = 0x0501,
SYSCALL_COMPATABILITY_ABI_VERSION = 0x0502,
SYSCALL_COMPATABILITY_ACTION = 0x0503,
SYSCALL_KERNEL_SCHEDULER_START = 0xFF00,
SYSCALL_KERNEL_PANIC = 0xFF01
SYSCALL_KERNEL_SCHEDULER_START = 0xFF00,
SYSCALL_KERNEL_PANIC = 0xFF01
} syscall_E;
typedef enum {

View File

@ -45,6 +45,10 @@ struct process_T {
page_map_T* page_map;
elf_executable_T* executable;
file_descriptor_array_T* fd_array;
// driver: If this process is a driver, this tells the ID
// of that driver within the driver management system.
uint32_t driver;
pipe_T* stdout;
pipe_T stdin;
@ -52,12 +56,12 @@ struct process_T {
bitmap_T waiting;
uint32_t num_threads;
uint32_t num_threads;
void* threads;
bitmap_T thread_ids;
process_T* parent;
process_T* childs;
process_T* childs; // TODO: Must be 'children'
process_T* prev;
process_T* next;
};

11
inc/utils/file.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef NOXOS_FILE_UTILS_H
#define NOXOS_FILE_UTILS_H
#include <utils/stdtypes.h>
uint64_t file_get_length (char *path);
uint64_t file_read_into_buffer (char *path, void *buffer, uint64_t offset, uint64_t num_bytes);
#endif

View File

@ -0,0 +1,15 @@
{
"drivers":
[
{
"name": "nxFAT32",
"author": "NOXOS Authors",
"category": "filesystem",
"type": "fat32",
"definition-path": "/modules/fat32/definition.json",
"binary-path": "/modules/fat32/driver.so"
}
]
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -1,8 +1,8 @@
{
"PS2_ACPI_VALIDATION": true,
"LOG_GRAPHICAL": false,
"LOG_GRAPHICAL": true,
"modules": {
"FAT32": "/initrd/modules/fat32.nxkm"
}
}
}

9
run.sh
View File

@ -5,6 +5,11 @@ EMUFLAGS="-no-reboot -machine type=q35,accel=kvm -cpu max -m 256M \
-drive file=build/noxos.iso \
-chardev stdio,id=log,logfile=noxos.log -serial chardev:log"
OVMF_PATH="/usr/share/ovmf/x64/OVMF.fd"
if [[ -f "/usr/share/OVMF/OVMF.fd" ]]; then
OVMF_PATH="/usr/share/OVMF/OVMF.fd"
fi
emulate_bios() {
echo "<=====| Emulating |=====>"
qemu-system-x86_64 $EMUFLAGS
@ -12,13 +17,13 @@ emulate_bios() {
emulate_uefi() {
echo "<=====| Emulating in UEFI mode |=====>"
qemu-system-x86_64 -bios /usr/share/ovmf/x64/OVMF.fd $EMUFLAGS
qemu-system-x86_64 -bios $OVMF_PATH $EMUFLAGS
}
# qemu will wait for gdb to connect, before booting
emulate_debug() {
echo "<=====| Emulating in debug mode |=====>"
qemu-system-x86_64 -s -S -bios /usr/share/ovmf/x64/OVMF.fd $EMUFLAGS
qemu-system-x86_64 -s -S -bios $OVMF_PATH $EMUFLAGS
}
case $1 in

View File

@ -35,39 +35,41 @@ void kernel_init(boot_info_T* boot_info) {
idt_init();
pic_init();
pit_set_divisor(PIT_DIVISOR);
paging_init();
CORE_INTERRUPTS_ENABLE
memory_allocator_init((void*)MEM_REGION_KERNEL_HEAP);
graphics_renderer_init(boot_info);
vfs_init(boot_info);
sysconfig_init();
if (g_sysconfig->log_graphical) graphical_log_init();
scheduler_init(boot_info);
acpi_init(boot_info);
module_manager_init();
drive_manager_init();
pci_init();
driver_manager_init();
tty_init();
ps2_controller_init();
}
void kmain(boot_info_T boot_info) {
limine_terminal_print(&boot_info, "Booting NoxOS...\n");
log(LOG_DEBUG, "Booting NoxOS");
limine_terminal_print(&boot_info, "Booting NOXOS...\n");
log(LOG_DEBUG, "Booting NOXOS");
kernel_init(&boot_info);

View File

@ -0,0 +1,28 @@
#include <drivers/device_manager.h>
#include <drivers/builtin/fs/vfs.h>
device_manager_T g_device_manager;
void device_manager_init() {
}
void device_remove(device_T *device) {
}
device_T * device_find(device_capabilities_T capabilities, uint32_t index, uint32_t *out_count) {
}

View File

@ -0,0 +1,140 @@
#include <drivers/driver_manager.h>
#include <drivers/builtin/json.h>
#include <utils/file.h>
#include <utils/memory.h>
#include <utils/logger.h>
driver_manager_T g_driver_manager;
void driver_manager_init()
{
log(LOG_INFO, "Starting Driver Manager!");
driver_manager_T manager;
manager.max_drivers = 1024;
manager.num_drivers = 0;
manager.drivers = memory_allocate(sizeof(driver_T) * manager.max_drivers);
g_driver_manager = manager;
}
void driver_manager_load_single_driver(char *driver_config_path)
{ // TODO
uint64_t len_file = file_get_length(driver_config_path);
void *content = memory_allocate(len_file+1);
file_read_into_buffer(driver_config_path, content, 0, len_file);
((uint8_t *) content)[len_file] = 0x00; // End it with a NULL-terminator
json_T *config_json = json_from_string(content);
// config_json->root_node->
}
void driver_manager_load_drivers(char *main_config_path)
{
}
driver_T * driver_manager_find_free_driver_slot()
{
driver_T *drivers = g_driver_manager.drivers;
uint32_t index = 0;
while(index < g_driver_manager.max_drivers)
{
if(drivers[index].category == DRIVER_UNKNOWN) return &drivers[index];
++index;
}
return NULL; // TODO: Resize array
}
uint32_t driver_manager_find_unused_driver_id()
{
driver_T *drivers = g_driver_manager.drivers;
uint32_t index = 0;
uint32_t id = 0;
while(id < 131072)
{
while(index < g_driver_manager.max_drivers)
{
if(drivers[index].id == id) break;
++index;
}
++id;
}
return id;
}
bool driver_is_configuration_valid(driver_configuration_T *configuration)
{
if(configuration->max_command_buffers == 0) return false;
if(configuration->max_command_buffers > 32) return false;
return true;
}
uint32_t driver_manager_register_driver(driver_configuration_T *configuration)
{
if(!driver_is_configuration_valid(configuration)) return 0; // TODO: Log
driver_T *driver = driver_manager_find_free_driver_slot();
driver->category = DRIVER_RESERVED;
driver->max_command_buffers = configuration->max_command_buffers;
driver->command_buffers = memory_allocate(sizeof(driver_T) * driver->max_command_buffers);
driver->id = driver_manager_find_unused_driver_id();
driver->category = configuration->category; // Activate the entry
return driver->id;
}
driver_T * driver_manager_resolve_driver_id(uint32_t id)
{
driver_T *drivers = g_driver_manager.drivers;
uint32_t index = 0;
while(index < g_driver_manager.max_drivers)
{
if(drivers[index].id == id) return &drivers[index];
++index;
}
return NULL;
}
driver_command_buffer_T * driver_find_free_command_buffer_slot(driver_T *driver)
{
driver_command_buffer_T *buffers = driver->command_buffers;
uint32_t index = 0;
while(index < driver->max_command_buffers)
{
if(buffers[index].mapping_address == NULL) return &buffers[index];
++index;
}
return NULL;
}
void driver_add_command_buffer(driver_T *driver, driver_command_buffer_config_T *configuration)
{
driver_command_buffer_T *buffer = driver_find_free_command_buffer_slot(driver);
buffer->command_capacity = configuration->num_commands;
buffer->mapping_address = configuration->mapping_address;
}
void driver_flush_command_buffer(driver_T *driver, void *buffer)
{
}

View File

@ -6,6 +6,8 @@
#include "utils/memory.h"
#include "proc/scheduler.h"
#include "drivers/builtin/fs/vfs.h"
#include <drivers/driver_manager.h>
#include "mm/page_frame.h"
#include "mm/region.h"
@ -25,9 +27,9 @@ void syscall_handle_nx_fopen(cpu_state_T* state) {
void syscall_handle_nx_fclose(cpu_state_T* state) {
file_descriptor_t fd = (file_descriptor_t)state->rdi; // arg1
file_descriptor_free(scheduler_get_current_process()->fd_array, fd);
state->rax = STATUS_SUCCESS;
}
@ -188,11 +190,13 @@ void syscall_handle_nx_mmap(cpu_state_T* state) {
}
void syscall_handle_nx_munmap(cpu_state_T* state) {
void* addr = (void*)state->rdi; // arg1
uint64_t n = state->rsi; // arg2
void *addr = (void *) state->rdi; // arg1
uint64_t n = state->rsi; // arg2
if ((uint64_t)&addr[n * PFRAME_SIZE] > MEM_REGION_KERNEL ||
(uint64_t)addr < MEM_REGION_PROCESS_USABLE) {
if(
((uint64_t) &addr[n * PFRAME_SIZE] > MEM_REGION_KERNEL) ||
((uint64_t) addr < MEM_REGION_PROCESS_USABLE)
) {
state->rax = STATUS_PERMISSION_DENIED;
return;
}
@ -264,6 +268,124 @@ void syscall_handle_nx_psignal(cpu_state_T* state) {
state->rax = STATUS_SUCCESS;
}
uint64_t ceil_to(uint64_t base, uint64_t factor)
{
return base + (base % factor);
}
uint64_t floor_to(uint64_t base, uint64_t factor)
{
return (base + (base % factor)) - factor;
}
bool are_process_pages_unused(process_T *process, void *address, uint64_t num_pages)
{
page_map_T *process_page_map = process->page_map;
uint64_t page_index = 0;
while(page_index < num_pages)
{
if(page_map_check_memory(
process_page_map,
&address[page_index * PFRAME_SIZE])
) {
return false;
}
++page_index;
}
return true;
}
bool are_process_memory_bytes_unused(process_T *process, void *address, uint64_t num_bytes)
{
uint64_t first_full_page_address = floor_to(address, PFRAME_SIZE);
uint64_t num_pages = ceil_to(address+num_bytes, PFRAME_SIZE) / PFRAME_SIZE;
return are_process_pages_unused(process, first_full_page_address, num_pages);
}
void mark_memory_region_for_process(process_T *process,
void *address, uint64_t num_pages,
bool read_write, bool executable)
{
uint64_t map_flags = 0;
if(read_write) map_flags |= PM_FLAG_READ_WRITE;
if(!executable) map_flags |= PM_FLAG_NO_EXECUTE;
page_map_T *process_page_map = process->page_map;
uint64_t page_index = 0;
while(page_index < num_pages)
{
page_map_map_memory(process_page_map, &address[page_index * PFRAME_SIZE], pframe_request(), map_flags);
++page_index;
}
}
// map_memory_for_user: Returns FALSE if the action failed and TRUE otherwise.
bool map_memory_for_process(process_T *process,void *address, uint64_t num_pages, bool read_write, bool executable)
{
if(address == NULL)
{
return false;
}
if(!are_process_pages_unused(process, address, num_pages))
{
return false;
}
mark_memory_region_for_process(process, address, num_pages, read_write, executable);
return true;
}
void syscall_handle_nx_drv_register_driver(cpu_state_T* state)
{
void *configuration = state->rdi;
process_T *caller = scheduler_get_current_process();
// Check permissions
if(are_process_memory_bytes_unused(caller, configuration, 256))
{
state->rax = STATUS_RESOURCE_NOT_AVAILABLE;
return;
}
caller->driver = driver_manager_register_driver(configuration);
}
void syscall_handle_nx_drv_create_command_buffer(cpu_state_T* state)
{
void *address = (void *) state->rdi;
uint64_t wanted_commands = state->rsi;
uint64_t num_pages = ceil_to(wanted_commands * DRIVER_COMMAND_SIZE, PFRAME_SIZE) / PFRAME_SIZE;
process_T *caller = scheduler_get_current_process();
driver_T *driver = driver_manager_resolve_driver_id(caller->driver);
if(map_memory_for_process(caller, address, num_pages, true, false))
{
driver_command_buffer_config_T buffer_config;
buffer_config.num_commands = num_pages;
buffer_config.mapping_address = address;
driver_add_command_buffer(driver, &buffer_config);
state->rax = address;
return;
}
// TODO: Select an address for the process and re-run map_memory_for_process.
state->rax = STATUS_RESOURCE_NOT_AVAILABLE;
}
void syscall_handle_nx_drv_flush_command_buffer(cpu_state_T* state)
{
void *command_buffer = state->rsi;
process_T *caller = scheduler_get_current_process();
driver_T *driver = driver_manager_resolve_driver_id(caller->driver);
driver_flush_command_buffer(driver, command_buffer);
}
cpu_state_T* syscall_handle(cpu_state_T* state) {
cpu_state_T* return_state = state;
syscall_group_E group_id = (state->rax & 0xFF00) >> 8;
@ -315,7 +437,7 @@ cpu_state_T* syscall_handle(cpu_state_T* state) {
break;
}
case SYSCALLS_PROC: {
case SYSCALLS_PROCESS: {
switch (state->rax) {
case SYSCALL_PROCESS_CREATE: {
syscall_handle_nx_pcreate(state);
@ -328,7 +450,23 @@ cpu_state_T* syscall_handle(cpu_state_T* state) {
}
break;
}
case SYSCALLS_DRIVER: {
switch(state->rax) {
case SYSCALL_DRIVER_REGISTER: {
syscall_handle_nx_drv_register_driver(state);
} break;
case SYSCALL_DRIVER_CREATE_COMMAND_BUFFER: {
syscall_handle_nx_drv_create_command_buffer(state);
} break;
case SYSCALL_DRIVER_FLUSH_COMMAND_BUFFER: {
syscall_handle_nx_drv_flush_command_buffer(state);
} break;
}
}
case SYSCALLS_RUNTIME_LINKER: {
break;
}
@ -369,4 +507,4 @@ cpu_state_T* syscall_handle(cpu_state_T* state) {
}
return return_state;
}
}

20
src/utils/file.c Normal file
View File

@ -0,0 +1,20 @@
#include <utils/file.h>
#include <drivers/builtin/fs/vfs.h>
uint64_t file_get_length(char *path)
{
vfs_node_T *file = vfs_resolve_path(&g_root_fs, path);
if(file == NULL) return 0;
return file->size;
}
uint64_t file_read_into_buffer(char *path, void *buffer, uint64_t offset, uint64_t num_bytes)
{
vfs_node_T *file = vfs_resolve_path(&g_root_fs, path);
if(file == NULL) return 0;
uint64_t actual_read_bytes = vfs_file_read(file, offset, num_bytes, buffer);
vfs_node_destruct(file);
return actual_read_bytes;
}