15#include <kernel/version.h>
28 .author =
"The PatchworkOS Authors",
29 .description =
"The PatchworkOS Kernel",
30 .version = OS_VERSION,
32 .osVersion = OS_VERSION,
51#define MODULE_SYMBOL_ALLOWED(type, binding, name) \
52 (((type) == STT_OBJECT || (type) == STT_FUNC) && ((binding) == STB_GLOBAL) && \
53 (strncmp(name, MODULE_RESERVED_PREFIX, MODULE_RESERVED_PREFIX_LENGTH) != 0))
57 module_t*
module = malloc(sizeof(module_t) + info->dataSize);
67 module->flags = MODULE_FLAG_NONE;
68 module->baseAddr = NULL;
70 module->procedure = NULL;
71 module->symbolGroupId = symbol_generate_group_id();
79 module->info.name = (char*)((uintptr_t)module->info.name + offset);
80 module->info.author = (char*)((uintptr_t)module->info.author + offset);
81 module->info.description = (char*)((uintptr_t)module->info.description + offset);
82 module->info.version = (char*)((uintptr_t)module->info.version + offset);
83 module->info.license = (char*)((uintptr_t)module->info.license + offset);
84 module->info.osVersion = (char*)((uintptr_t)module->info.osVersion + offset);
85 module->info.deviceTypes = (char*)((uintptr_t)module->info.deviceTypes + offset);
137 LOG_ERR(
"call to load event for module '%s' failed\n", module->
info.
name);
140 module->flags |= MODULE_FLAG_LOADED;
152 module->procedure(&unloadEvent);
153 module->flags &= ~MODULE_FLAG_LOADED;
215 handler->module =
module;
220 .deviceAttach.type = device->
type,
221 .deviceAttach.name = device->
name,
225 LOG_ERR(
"call to attach event for module '%s' failed\n", module->
info.
name);
239 .deviceDetach.type = handler->
device->type,
240 .deviceDetach.name = handler->
device->name,
242 handler->module->procedure(&detachEvent);
260 while (str[len] !=
'\0' && str[len] !=
';' && len < outSize - 1)
273 LOG_ERR(
"module info string is of invalid size %zu\n", totalSize);
289 LOG_ERR(
"failed to parse module name\n");
293 offset += parsed + 1;
299 LOG_ERR(
"failed to parse module author\n");
302 info->author[parsed] =
'\0';
303 offset += parsed + 1;
305 info->description = &
info->data[offset];
309 LOG_ERR(
"failed to parse module description\n");
312 info->description[parsed] =
'\0';
313 offset += parsed + 1;
315 info->version = &
info->data[offset];
319 LOG_ERR(
"failed to parse module version\n");
322 info->version[parsed] =
'\0';
323 offset += parsed + 1;
325 info->license = &
info->data[offset];
329 LOG_ERR(
"failed to parse module license\n");
332 info->license[parsed] =
'\0';
333 offset += parsed + 1;
335 info->osVersion = &
info->data[offset];
339 LOG_ERR(
"failed to parse module OS version\n");
342 info->osVersion[parsed] =
'\0';
343 offset += parsed + 1;
347 LOG_ERR(
"module '%s' requires OS version '%s' but running version is '%s'\n",
info->
name,
info->osVersion,
352 size_t deviceTypesLength = totalSize - offset;
353 info->deviceTypes = &
info->data[offset];
354 strncpy_s(
info->deviceTypes, deviceTypesLength + 1, &moduleInfo[offset], deviceTypesLength + 1);
356 info->dataSize = totalSize + 1;
372 const char* filename)
389 if (fileData ==
NULL)
402 LOG_ERR(
"failed to validate ELF file '%s' while reading module metadata\n", filename);
412 LOG_ERR(
"failed to find valid module info section in ELF file '%s'\n", filename);
460 if (symbolEntry ==
NULL)
477 LOG_ERR(
"symbol name collision for '%s' in module '%s'\n", symName, path);
489 const char* ptr =
file->info->deviceTypes;
503 if (cachedDevice ==
NULL)
506 if (cachedDevice ==
NULL)
521 if (deviceEntry ==
NULL)
603 if (readCount ==
ERR)
625 LOG_ERR(
"skipping invalid module file '%s'\n",
buffer[i].path);
662 module->flags |= MODULE_FLAG_GC_REACHABLE;
677 module->flags |= MODULE_FLAG_GC_REACHABLE;
707 module->flags &= ~MODULE_FLAG_GC_REACHABLE;
718 module = CONTAINER_OF(list_pop_first(&unreachables), module_t, gcEntry);
730 if (kernelModule ==
NULL)
746 void* symAddr = (
void*)sym->
st_value;
755 LOG_INFO(
"loaded %llu kernel symbols\n", index);
765 const char* deviceTypePtr =
info->deviceTypes;
766 while (*deviceTypePtr !=
'\0')
774 deviceTypePtr += parsed + 1;
799 uint64_t moduleMemSize = maxVaddr - minVaddr;
802 module->baseAddr = vmm_alloc(NULL, NULL, moduleMemSize, PML_PRESENT | PML_WRITE | PML_GLOBAL, VMM_ALLOC_OVERWRITE);
807 module->size = moduleMemSize;
808 module->procedure = (module_procedure_t)((uintptr_t)module->baseAddr + (elf->header->e_entry - minVaddr));
834 void* symAddr = (
void*)((
uintptr_t)
module->baseAddr + (sym->st_value - minVaddr));
837 LOG_ERR(
"failed to add symbol '%s' to module '%s'\n", symName, module->
info.
name);
858 if (cacheEntry ==
NULL)
860 LOG_ERR(
"no cached module found for symbol '%s'\n", symbolName);
877 if (dependency ==
NULL)
913 LOG_ERR(
"failed to resolve symbol '%s' after loading dependency\n", symbolName);
920 if (existingModule !=
NULL)
922 return symbolInfo.
addr;
926 if (dependency ==
NULL)
932 if (dependency->module ==
NULL)
941 if (existingDependency->module == dependency->module)
944 return symbolInfo.
addr;
949 return symbolInfo.
addr;
962 .filename = filename,
982 if (existingModule !=
NULL)
985 return existingModule;
988 module_t*
module = module_new(file.info);
997 LOG_INFO(
"loading '%s' version %s by %s\n", module->info.name, module->info.version, module->info.author);
998 LOG_DEBUG(
" description: %s\n licence: %s\n", module->info.description, module->info.license);
1002 if (loadResult ==
ERR)
1011 if (dependency ==
NULL)
1036 LOG_DEBUG(
"finished loading module '%s'\n", module->info.name);
1072 if (cachedDevice ==
NULL)
1089 LOG_ERR(
"device '%s' type mismatch (expected '%s', got '%s')\n", name, device->
type, type);
1113 module_t*
module = module_get_or_load(deviceEntry->path, dir, type);
1116 LOG_ERR(
"failed to load module '%s' for device '%s'\n", deviceEntry->
path, name);
1121 if (handler ==
NULL)
1137 LOG_DEBUG(
"added handler with module '%s' and device '%s'\n", handler->module->info.name, name);
1186 if (deviceTypes ==
NULL || type ==
NULL)
1191 size_t idLen =
strlen(type);
1192 const char* pos = deviceTypes;
1196 bool isStart = (pos == deviceTypes) || (*(pos - 1) ==
';');
1197 bool isEnd = (pos[idLen] ==
';') || (pos[idLen] ==
'\0');
1199 if (isStart && isEnd)
#define MAX_PATH
Maximum length of filepaths.
#define assert(expression)
#define PATHNAME(string)
Helper to create a pathname.
boot_info_t * boot_info_get(void)
Gets the boot info structure.
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
#define LOG_ERR(format,...)
#define LOG_INFO(format,...)
#define LOG_DEBUG(format,...)
void * vmm_unmap(space_t *space, void *virtAddr, uint64_t length)
Unmaps virtual memory from a given address space.
uint64_t symbol_resolve_name(symbol_info_t *outSymbol, const char *name)
Resolve a symbol by name.
uint64_t symbol_group_id_t
Symbol group identifier type.
uint64_t symbol_add(const char *name, void *addr, symbol_group_id_t groupId, Elf64_Symbol_Binding binding, Elf64_Symbol_Type type)
Add a symbol to the kernel symbol table.
void symbol_remove_group(symbol_group_id_t groupId)
Remove all symbols from the kernel symbol table in the given group.
bool module_device_types_contains(const char *deviceTypes, const char *type)
Check if a list of device types contains a specific device type.
void module_device_detach(const char *name)
Notify the module system of a device being detached.
module_load_flags_t
Module load flags.
#define MODULE_INFO_SECTION
#define MODULE_DIR
The directory where the kernel will look for modules.
void module_init_fake_kernel_module(void)
Initialize a fake module representing the kernel itself.
uint64_t module_device_attach(const char *type, const char *name, module_load_flags_t flags)
Notify the module system of a device being attached.
@ MODULE_EVENT_DEVICE_ATTACH
@ MODULE_EVENT_DEVICE_DETACH
@ MODULE_FLAG_LOADED
If set, the module has received the MODULE_EVENT_LOAD event.
@ MODULE_FLAG_GC_REACHABLE
Used by the GC to mark reachable modules.
@ MODULE_FLAG_GC_PINNED
If set, the module will never be collected by the GC, used for the fake kernel module.
@ MODULE_LOAD_ALL
If set, will load all modules matching the device type.
@ MODULE_MAX_DEVICE_STRING
process_t * sched_process(void)
Retrieves the process of the currently running thread.
#define MUTEX_CREATE(name)
Create a mutex initializer.
#define MUTEX_SCOPE(mutex)
Acquires a mutex for the reminder of the current scope.
void map_clear(map_t *map)
Clear all entries from the map.
void map_entry_init(map_entry_t *entry)
Initialize a map entry.
static map_key_t map_key_uint64(uint64_t uint64)
Create a map key from a uint64_t.
uint64_t map_insert(map_t *map, const map_key_t *key, map_entry_t *value)
Insert a key-value pair into the map.
void map_remove(map_t *map, map_entry_t *entry)
Remove a entry from the map.
#define MAP_ENTRY_PTR_IS_VALID(entryPtr)
Check if a map entry pointer is valid (not NULL or tombstone).
map_entry_t * map_get(map_t *map, const map_key_t *key)
Get a value from the map by key.
static map_key_t map_key_string(const char *str)
Create a map key from a string.
#define MAP_CREATE()
Create a map initializer.
#define UNREF_DEFER(ptr)
RAII-style cleanup for scoped references.
uint64_t vfs_seek(file_t *file, int64_t offset, seek_origin_t origin)
Seek in a file.
uint64_t vfs_getdents(file_t *file, dirent_t *buffer, uint64_t count)
Get directory entries from a directory file.
file_t * vfs_open(const pathname_t *pathname, process_t *process)
Open a file.
uint64_t vfs_read(file_t *file, void *buffer, uint64_t count)
Read from a file.
file_t * vfs_openat(const path_t *from, const pathname_t *pathname, process_t *process)
Open a file relative to another path.
#define EEXIST
File exists.
#define EINVAL
Invalid argument.
#define errno
Error number variable.
#define ENODEV
No such device.
#define EILSEQ
Illegal byte sequence.
#define ELF64_ST_TYPE(i)
Extract the type from st_info.
Elf64_Symbol_Binding
Symbol binding values stored in st_info.
#define ELF64_ST_BIND(i)
Extract the binding from st_info.
uint64_t elf64_relocate(const Elf64_File *elf, Elf64_Addr base, Elf64_Off offset, void *(*resolve_symbol)(const char *name, void *private), void *private)
Perform relocations on an ELF file loaded into memory.
void elf64_load_segments(const Elf64_File *elf, Elf64_Addr base, Elf64_Off offset)
Load all loadable segments of an ELF file into memory.
uint64_t elf64_validate(Elf64_File *elf, void *data, uint64_t size)
Validate a files content and initalize a ELF64_File structure using it.
Elf64_Sym * elf64_get_symbol_by_index(const Elf64_File *elf, Elf64_Xword symbolIndex)
Get a symbol by its index from the symbol table.
const char * elf64_get_symbol_name(const Elf64_File *elf, const Elf64_Sym *symbol)
Get the name of a symbol.
void elf64_get_loadable_bounds(const Elf64_File *elf, Elf64_Addr *minAddr, Elf64_Addr *maxAddr)
Get the loadable virtual memory bounds of an ELF file.
uint64_t Elf64_Addr
ELF64 Unsigned program address.
Elf64_Shdr * elf64_get_section_by_name(const Elf64_File *elf, const char *name)
Get a section by its name.
Elf64_Symbol_Type
Symbol type values stored in st_info.
@ SHN_UNDEF
Undefined section.
@ SHN_ABS
Specifies absolute values for the corresponding reference.
#define LIST_FOR_EACH(elem, list, member)
Iterates over a list.
static list_entry_t * list_first(list_t *list)
Gets the first entry in the list without removing it.
static list_entry_t * list_pop_first(list_t *list)
Pops the first entry from the list.
static uint64_t list_length(list_t *list)
Gets the length of the list.
static void list_push_back(list_t *list, list_entry_t *entry)
Pushes an entry to the end of the list.
#define LIST_CREATE(name)
Creates a list initializer.
static list_entry_t * list_pop_last(list_t *list)
Pops the last entry from the list.
static void list_remove(list_t *list, list_entry_t *entry)
Removes a list entry from its current list.
static bool list_is_empty(list_t *list)
Checks if a list is empty.
static void list_entry_init(list_entry_t *entry)
Initializes a list entry.
static void list_init(list_t *list)
Initializes a list.
#define PAGE_SIZE
The size of a memory page in bytes.
#define NULL
Pointer error value.
#define ERR
Integer error value.
#define CONTAINER_OF(ptr, type, member)
Container of macro.
#define CONTAINER_OF_SAFE(ptr, type, member)
Safe container of macro.
EFI_PHYSICAL_ADDRESS buffer
errno_t memcpy_s(void *_RESTRICT s1, rsize_t s1max, const void *_RESTRICT s2, rsize_t n)
static module_cached_device_t * module_cache_lookup_device_type(const char *type)
static module_t * module_new(module_info_t *info)
static void module_gc_collect(void)
static uint64_t module_call_load_event(module_t *module)
static map_t symbolCache
Key = symbol name, value = module_cached_symbol_t*.
static module_device_handler_t * module_handler_add(module_t *module, module_device_t *device)
static uint64_t module_file_read(module_file_t *outFile, const path_t *dirPath, process_t *process, const char *filename)
#define MODULE_SYMBOL_ALLOWED(type, binding, name)
static void module_gc_sweep_unreachable(module_t *module, list_t *unreachables)
static uint64_t module_load_and_relocate_elf(module_t *module, Elf64_File *elf, module_load_ctx_t *ctx)
static uint64_t module_cache_device_types_add(module_file_t *file, const char *path)
static module_t * module_find_by_name(const char *name)
static uint64_t module_string_copy(const char *str, char *out, size_t outSize)
Copy a string up to either a null-terminator or a semicolon into the output buffer.
static map_t modulesMap
Key = module name, value = module_t*.
static void module_call_unload_event(module_t *module)
static map_t deviceCache
Key = device type, value = module_cached_device_t*.
static list_t modulesList
static bool module_info_supports_device(const module_info_t *info, const char *type)
static void module_cache_clear(void)
static uint64_t module_load_dependency(module_load_ctx_t *ctx, const char *symbolName)
static map_t deviceMap
Key = device name, value = module_device_t*.
static module_info_t * module_info_parse(const char *moduleInfo)
static module_cached_symbol_t * module_cache_lookup_symbol(const char *name)
static void module_handler_remove(module_device_handler_t *handler)
static void * module_resolve_symbol_callback(const char *name, void *private)
static uint64_t module_cache_build(void)
static module_info_t fakeKernelModuleInfo
static uint64_t module_cache_symbols_add(module_file_t *file, const char *path)
static void module_free(module_t *module)
static module_device_t * module_device_new(const char *type, const char *name)
static map_t providerMap
Key = symbol_group_id_t, value = module_t*. Used to find which module provides which symbols.
static void module_gc_mark_reachable(module_t *module)
static module_t * module_find_provider(symbol_group_id_t groupId)
static module_device_t * module_device_get(const char *name)
static void module_device_free(module_device_t *device)
static module_t * module_get_or_load(const char *filename, file_t *dir, const char *type)
static void module_file_deinit(module_file_t *file)
static const path_flag_t flags[]
__UINTPTR_TYPE__ uintptr_t
_PUBLIC void * malloc(size_t size)
_PUBLIC void free(void *ptr)
_PUBLIC char * strerror(int errnum)
char * strdup(const char *src)
_PUBLIC char * strstr(const char *s1, const char *s2)
_PUBLIC int strncmp(const char *s1, const char *s2, size_t n)
_PUBLIC size_t strlen(const char *s)
_PUBLIC int strcmp(const char *s1, const char *s2)
errno_t strncpy_s(char *_RESTRICT s1, rsize_t s1max, const char *_RESTRICT s2, rsize_t n)
size_t strnlen_s(const char *s, size_t maxsize)
ELF File Helper structure.
Elf64_Ehdr * header
The data in the file, pointed to the start of the ELF header.
Elf64_Xword sh_size
Section size in bytes.
Elf64_Off sh_offset
Section's file offset in bytes.
ELF64 Symbol Table Entry.
char name[MAX_NAME]
Constant after creation.
Module device cache entry structure.
char path[MAX_PATH]
Path to the module supporting the device.
Module device cache entry structure.
list_t entries
List of module_cached_device_entry_t.
Module symbol cache entry structure.
char * modulePath
Path to the module defining the symbol.
module_t *module_device_t * device
char name[MODULE_MAX_DEVICE_STRING]
char type[MODULE_MAX_DEVICE_STRING]
list_t handlers
List of module_device_handler_t representing modules handling this device.
module_t * current
The module whose dependencies are currently being loaded.
list_t deviceHandlers
List of module_device_handler_t representing devices this module handles.
map_entry_t mapEntry
Entry for the global module map.
uint64_t size
The size of the modules loaded image in memory.
list_t dependencies
List of module_dependency_t representing modules this module depends on.
list_entry_t gcEntry
Entry used for garbage collection.
list_entry_t listEntry
Entry for the global module list.
void * baseAddr
The address where the modules image is loaded in memory.
symbol_group_id_t symbolGroupId
The symbol group ID for the module's symbols.
module_procedure_t procedure
The module's procedure function and entry point.
map_entry_t providerEntry
Entry for the module provider map.
list_entry_t loadEntry
Entry used while loading modules.
Symbol information structure.
symbol_group_id_t groupId