Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,269 changes: 1,621 additions & 1,648 deletions external/include/stb_sprintf.h

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions include/debug/debug_stdio.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

void dputc(char c);
void dputs(const char* s);
void dputgap(unsigned int gap_size);

[[gnu::format(printf, 1, 2)]]
void dprintf(const char* fmt, ...);
Expand All @@ -12,6 +13,7 @@ void dprintf(const char* fmt, ...);
#define DEBUG_PUTC(c) dputc(c)
#define DEBUG_PUTS(s) dputs(s)
#define DEBUG_PRINTF(fmt, ...) dprintf(fmt __VA_OPT__(, ) __VA_ARGS__)
#define DEBUG_PUTGAP(n) dputgap(n)

#else

Expand All @@ -21,6 +23,7 @@ void dprintf(const char* fmt, ...);
#define DEBUG_PUTC(c)
#define DEBUG_PUTS(s)
#define DEBUG_PRINTF(fmt, ...) noop_printf(fmt __VA_OPT__(, ) __VA_ARGS__)
#define DEBUG_PUTGAP(n)

#endif
#endif
34 changes: 34 additions & 0 deletions include/drivers/dt/dt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#ifndef DT_H
#define DT_H

#include <drivers/dt/dt_node.h>
#include <stdbigos/buffer.h>
#include <stdbigos/types.h>

// Initializes the device tree driver, the second argument specifies, whether the machine operates on big endian numbers
// natively
// TODO: Currently parses the entire FDT at init, maybe change to lazy parsing later
// Builds the actual tree structure handleable by the driver, 0 if success, <0 if error
int dt_init(const void* fdt, endianness_t machine_big_endian);
Comment thread
Kamilosok marked this conversation as resolved.

// Frees all memory used by the arena
void dt_cleanup(void);

// Find a node by full path in the tree, dt_node_t ptr if success, nullptr if error
dt_node_t* dt_node_find(const char* path);

// Get a node's name, const char ptr name if success, nullptr if error
const char* dt_node_get_name(const dt_node_t* node);

// Get node's child count, >=0 if success, <0 if error
int dt_node_child_count(const dt_node_t* node);

// Get a node's next sibling, dt_node_t ptr if success, nullptr if error or node is the last child
dt_node_t* dt_get_next_child(const dt_node_t* node);

// Returns the pointer to the root of the parsed device tree if success, nullptr if error
dt_node_t* dt_get_root(void);

void dt_reset_root(void);

#endif
23 changes: 23 additions & 0 deletions include/drivers/dt/dt_node.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef DT_NODE_H
#define DT_NODE_H

#include <stdbigos/types.h>

// Both prop and node structures are lists with outside pointers only to the first element
typedef struct dt_prop_t {
const char* name;
const void* value;
u32 data_length;
struct dt_prop_t* next_prop;
} dt_prop_t;

typedef struct dt_node_t {
const char* name;
u32 phandle;
struct dt_prop_t* props;
struct dt_node_t* parent;
struct dt_node_t* first_child;
struct dt_node_t* next_sibling;
} dt_node_t;

#endif
20 changes: 20 additions & 0 deletions include/drivers/dt/dt_props.h
Comment thread
qbojj marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef DT_PROPS_H
#define DT_PROPS_H

#include <drivers/dt/dt_node.h>
#include <stdbigos/types.h>

// Can be used to check for bool-type props
// Find a property of node by a name, dt_prop_t ptr if success, nullptr if error
dt_prop_t* dt_find_prop(const dt_node_t* node, const char* name);

// Get a buffer of node's name value, the buffer's BUFFER_ERROR field will have any potential errors
buffer_t dt_prop_get_buffer(const dt_node_t* node, const char* name);

// Print the node's properties
void dt_print_props(const dt_node_t* node, u8 depth);

// Print the entire subtree with properties
void dt_print_tree(const dt_node_t* node, u8 depth);

#endif
12 changes: 12 additions & 0 deletions include/stdbigos/bitutils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef _STDBIGOS_BITUTILS_H
#define _STDBIGOS_BITUTILS_H

#include <stdbigos/types.h>

u32 read_be32(const void* addr);

u64 read_be64(const void* addr);

u32 align_u32(u32 num, u32 align);

#endif
45 changes: 45 additions & 0 deletions include/stdbigos/buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#ifndef _STDBIGOS_BUFFER_H
#define _STDBIGOS_BUFFER_H

#include <stdbigos/types.h>

// Error codes
typedef enum : u32 {
BUF_ERR_OK = 0,
BUF_ERR_OUT_OF_BOUNDS = 1,
BUF_ERR_FETCH = 2
} buffer_error_t;

typedef struct buffer_t {
const void* data;
size_t size;
buffer_error_t error;
} buffer_t;

// Helpers to create buffers
static inline buffer_t make_buffer(const void* data, size_t size) {
buffer_t buf = {.data = data, .size = size, .error = BUF_ERR_OK};
return buf;
}

static inline buffer_t make_buffer_err(const void* data, size_t size, buffer_error_t error) {
buffer_t buf = {.data = data, .size = size, .error = error};
return buf;
}

// Read big-endian 32-bit from buffer at given offset
int buffer_read_u32_be(buffer_t buf, size_t offset, u32* out);

// Read big-endian 64-bit from buffer at given offset
int buffer_read_u64_be(buffer_t buf, size_t offset, u64* out);

// Read little-endian 32-bit from buffer at given offset
int buffer_read_u32_le(buffer_t buf, size_t offset, u32* out);

// Read little-endian 64-bit from buffer at given offset
int buffer_read_u64_le(buffer_t buf, size_t offset, u64* out);

// Read a zero-terminated C-string from buf at offset
int buffer_read_cstring(buffer_t buf, size_t offset, const char** out_str);

#endif
5 changes: 5 additions & 0 deletions include/stdbigos/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ typedef unsigned long reg_t;
typedef signed long ireg_t;
static_assert(sizeof(reg_t) * 8 == __riscv_xlen);

typedef enum {
ENDIAN_LITTLE = 0,
ENDIAN_BIG = 1,
} endianness_t;

#endif
9 changes: 9 additions & 0 deletions src/drivers/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
add_subdirectory(dt)

SETUP_LIBRARY(drivers)
target_link_libraries(drivers INTERFACE dt)

target_include_directories(drivers
INTERFACE
${CMAKE_SOURCE_DIR}/include/drivers
)
13 changes: 13 additions & 0 deletions src/drivers/dt/CMakeLists.txt
Comment thread
Kamilosok marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
SETUP_LIBRARY(dt)

target_link_libraries(dt PRIVATE stdbigos)

if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_link_libraries(dt PRIVATE Debug)
endif()


target_include_directories(dt
PUBLIC
${CMAKE_SOURCE_DIR}/include/drivers/dt
)
135 changes: 135 additions & 0 deletions src/drivers/dt/dt_access.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#include <debug/debug_stdio.h>
#include <drivers/dt/dt.h>
#include <drivers/dt/dt_node.h>
#include <drivers/dt/dt_props.h>
#include <stdbigos/bitutils.h>
#include <stdbigos/string.h>

#include "dt_alloc.h"
#include "dt_parser.h"

static dt_node_t* find_child_by_name_s(dt_node_t* parent, const char* name) {
for (dt_node_t* child = parent->first_child; child; child = child->next_sibling) {
if (strcmp(child->name, name) == 0) {
return child;
}
}
return nullptr;
}

dt_node_t* dt_node_find(const char* path) {
dt_node_t* root_node = dt_get_root();

if (!path || path[0] != '/' || !root_node)
return nullptr;

if (strcmp(path, "/") == 0)
return root_node;

dt_node_t* current = root_node;
const char* p = path + 1;

while (*p) {
const char* slash = p;
while (*slash && *slash != '/') slash += 1;

uintptr_t len = slash - p;
if (len == 0)
break;

char segment[64];
if (len >= (u32)sizeof(segment))
return nullptr;

memcpy(segment, p, len);
segment[len] = '\0';

current = find_child_by_name_s(current, segment);
if (!current)
return nullptr;

p = (*slash == '/') ? slash + 1 : slash;
}

return current;
}

const char* dt_node_get_name(const dt_node_t* node) {
if (!node)
return nullptr;

return node->name;
}

int dt_node_child_count(const dt_node_t* n) {
int c = 0;
for (dt_node_t* ch = n->first_child; ch; ch = ch->next_sibling) c++;
return c;
}

dt_node_t* dt_get_next_child(const dt_node_t* node) {
if (!node)
return nullptr;

// If there's no next sibling, it will automatically return a nullptr, as per documentation
return node->next_sibling;
}

dt_prop_t* dt_find_prop(const dt_node_t* node, const char* name) {
if (!node || !name)
return nullptr;

for (dt_prop_t* prop = node->props; prop; prop = prop->next_prop) {
if (strcmp(prop->name, name) == 0) {
return prop;
}
}

return nullptr;
}

buffer_t dt_prop_get_buffer(const dt_node_t* node, const char* name) {
buffer_t buffer = make_buffer_err(nullptr, 0, BUF_ERR_FETCH);

dt_prop_t* prop = dt_find_prop(node, name);
if (!prop || !prop->value)
return buffer;

buffer.data = prop->value;
buffer.size = prop->data_length;
buffer.error = BUF_ERR_OK;

return buffer;
}

#ifndef NDEBUG

// Use only in debug preset
void dt_print_props(const dt_node_t* node, [[maybe_unused]] u8 depth) {
dt_prop_t* next = node->props;
while (next) {
dputgap(depth);
dprintf("%s\n", next->name);
next = next->next_prop;
}
}

// Use only in debug preset
void dt_print_tree(const dt_node_t* node, [[maybe_unused]] u8 depth) {
dputgap(depth);
dprintf("NODE: %s\n", node->name);
dputgap(depth);

dprintf("PROPERTIES:\n");
dt_print_props(node, depth + 1);
dputc('\n');
dt_node_t* next = node->first_child;
while (next) {
dt_print_tree(next, depth + 1);
next = next->next_sibling;
}
}
Comment thread
Kamilosok marked this conversation as resolved.
#else
void dt_print_props(const dt_node_t* node, [[maybe_unused]] u8 depth);
void dt_print_tree(const dt_node_t* node, [[maybe_unused]] u8 depth);
#endif
53 changes: 53 additions & 0 deletions src/drivers/dt/dt_alloc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include "dt_alloc.h"

#include <stdbigos/bitutils.h>
#include <stdbigos/buffer.h>
#include <stdbigos/types.h>

// Arena proper
static u8 dt_arena_buffer[DT_ARENA_SIZE];

static u32* arena_start = nullptr;

// Arena size in bytes
static u32 arena_size;

// Used area size in bytes
static u32 arena_offset;

u8* dt_get_arena_buffer(void) {
return dt_arena_buffer;
}

int dt_arena_init(void* start, u32 size) {
if (start == nullptr || size == 0)
return -1;

arena_start = (u32*)start;
arena_size = size;
arena_offset = 0;

return 0;
}

void* dt_alloc(u32 size) {
// May not be needed
if (size == 0)
return nullptr;

// Align to 4 bytes
u32 align = align_u32(size, 4);

if (arena_offset + align > arena_size)
return nullptr;

void* new_block = arena_start + arena_offset;
arena_offset += align;

return new_block;
}

// Invalidates previously allocated blocks by ignoring them
void dt_arena_reset(void) {
arena_offset = 0;
}
Loading