ringbuffer, guid, iovec stuff, and mmio

This commit is contained in:
2026-10-14 23:26:30 +02:00
parent 4c781f31ad
commit d7857f18cd
13 changed files with 701 additions and 12 deletions
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <stdint.h> // IWYU pragma: keep
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define cpu_to_le16(x) ((uint16_t)(x))
#define cpu_to_le32(x) ((uint32_t)(x))
#define cpu_to_le64(x) ((uint64_t)(x))
#define cpu_to_be16(x) __builtin_bswap16(x)
#define cpu_to_be32(x) __builtin_bswap32(x)
#define cpu_to_be64(x) __builtin_bswap64(x)
#else
#define cpu_to_le16(x) __builtin_bswap16(x)
#define cpu_to_le32(x) __builtin_bswap32(x)
#define cpu_to_le64(x) __builtin_bswap64(x)
#define cpu_to_be16(x) ((uint16_t)(x))
#define cpu_to_be32(x) ((uint32_t)(x))
#define cpu_to_be64(x) ((uint64_t)(x))
#endif
#define le16_to_cpu(x) cpu_to_le16(x)
#define le32_to_cpu(x) cpu_to_le32(x)
#define le64_to_cpu(x) cpu_to_le64(x)
#define be16_to_cpu(x) cpu_to_be16(x)
#define be32_to_cpu(x) cpu_to_be32(x)
#define be64_to_cpu(x) cpu_to_be64(x)
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
struct [[gnu::packed]] guid {
uint32_t time_low;
uint16_t time_mid;
uint16_t time_hi_version;
uint8_t clock_seq[2];
uint8_t node[6];
};
static_assert(sizeof(struct guid) == 16);
constexpr size_t GUID_STR_LEN = 37;
bool guid_is_zero(const struct guid* guid);
bool guid_equal(const struct guid* a, const struct guid* b);
void guid_format(const struct guid* guid, char out[static GUID_STR_LEN]);
+16 -5
View File
@@ -8,31 +8,42 @@
typedef struct __iovec iovec_t;
struct iovec_iter {
typedef struct {
iovec_t* iovecs;
size_t iovecs_len;
size_t total_len;
size_t total_offset;
size_t current_idx;
size_t current_offset;
};
// Whether the iovecs point to user memory.
bool user;
} iovec_iter_t;
static inline size_t iovec_iter_len(struct iovec_iter* iter) {
static inline size_t iovec_iter_len(iovec_iter_t* iter) {
return iter->total_len - iter->total_offset;
}
// Creates an iovec iterator over kernel memory.
static inline struct iovec_iter iovec_iter_kernel(iovec_t* iovecs, size_t count) {
static inline iovec_iter_t iovec_iter_kernel(iovec_t* iovecs, size_t count) {
size_t total = 0;
for (size_t i = 0; i < count; i++)
total += iovecs[i].len;
return (struct iovec_iter){
return (iovec_iter_t){
.iovecs = iovecs,
.iovecs_len = count,
.total_len = total,
.total_offset = 0,
.current_idx = 0,
.current_offset = 0,
.user = false,
};
}
// Copies up to `len` bytes from `src` into the iterator and advances it.
// Returns the amount of bytes actually copied in `out_copied`.
errno_t iovec_iter_copy_to(iovec_iter_t* iter, const void* src, size_t len, size_t* out_copied);
// Copies up to `len` bytes out of the iterator into `dst` and advances it.
// Returns the amount of bytes actually copied in `out_copied`.
errno_t iovec_iter_copy_from(iovec_iter_t* iter, void* dst, size_t len, size_t* out_copied);
+103
View File
@@ -1,5 +1,6 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
static inline uint8_t mmio_read8(void* ptr) {
@@ -33,3 +34,105 @@ static inline void mmio_write32(void* ptr, uint32_t value) {
static inline void mmio_write64(void* ptr, uint64_t value) {
*((volatile uint64_t*)ptr) = value;
}
enum mmio_endian : uint8_t {
MMIO_NATIVE = 0,
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
MMIO_LE = 0,
MMIO_BE = 1,
#else
MMIO_BE = 0,
MMIO_LE = 1,
#endif
};
typedef struct {
size_t offset;
enum mmio_endian endian;
} mmio_reg8;
typedef struct {
size_t offset;
enum mmio_endian endian;
} mmio_reg16;
typedef struct {
size_t offset;
enum mmio_endian endian;
} mmio_reg32;
typedef struct {
size_t offset;
enum mmio_endian endian;
} mmio_reg64;
typedef struct {
mmio_reg8 reg;
uint8_t shift;
uint8_t width;
} mmio_field8;
typedef struct {
mmio_reg16 reg;
uint8_t shift;
uint8_t width;
} mmio_field16;
typedef struct {
mmio_reg32 reg;
uint8_t shift;
uint8_t width;
} mmio_field32;
typedef struct {
mmio_reg64 reg;
uint8_t shift;
uint8_t width;
} mmio_field64;
#define MMIO_REG_ZERO(reg) \
_Generic((reg), mmio_reg8: (uint8_t)0, mmio_reg16: (uint16_t)0, mmio_reg32: (uint32_t)0, mmio_reg64: (uint64_t)0)
#define MMIO_REG_TYPE(reg) typeof(MMIO_REG_ZERO(reg))
#define MMIO_FIELD_TYPE(field) MMIO_REG_TYPE((field).reg)
#define mmio_bswap(value) \
_Generic( \
(value), \
uint8_t: (value), \
uint16_t: __builtin_bswap16(value), \
uint32_t: __builtin_bswap32(value), \
uint64_t: __builtin_bswap64(value) \
)
#define mmio_width_mask(width) ((width) >= 64 ? UINT64_MAX : ((uint64_t)1 << (width)) - 1)
// Reads an MMIO register.
#define mmio_reg_read(base, reg) \
({ \
auto __mmio_rr = (reg); \
typedef MMIO_REG_TYPE(__mmio_rr) __mmio_rt; \
__mmio_rt __mmio_rv = *(volatile __mmio_rt*)((uint8_t*)(base) + __mmio_rr.offset); \
(__mmio_rt)(__mmio_rr.endian ? mmio_bswap(__mmio_rv) : __mmio_rv); \
})
// Writes a value to an MMIO register.
#define mmio_reg_write(base, reg, value) \
({ \
auto __mmio_rw = (reg); \
static_assert( \
sizeof(value) <= sizeof(MMIO_REG_TYPE(reg)), \
"High bits of the value might get truncated. Explicitly cast if this is intended." \
); \
typedef MMIO_REG_TYPE(__mmio_rw) __mmio_wt; \
__mmio_wt __mmio_wv = (value); \
*(volatile __mmio_wt*)((uint8_t*)(base) + __mmio_rw.offset) = \
__mmio_rw.endian ? mmio_bswap(__mmio_wv) : __mmio_wv; \
})
// Shifts and masks the bits of `value` to the position it needs to be stored in the register.
#define mmio_field(field, value) ((MMIO_FIELD_TYPE(field))(((value) & mmio_width_mask((field).width)) << (field).shift))
// Returns the contents of a `field` from a `value`.
#define mmio_field_get(field, value) \
((MMIO_FIELD_TYPE(field))(((value) >> (field).shift) & mmio_width_mask((field).width)))
+51
View File
@@ -0,0 +1,51 @@
#pragma once
#include <zinnia/errno.h>
#include <stddef.h>
#include <stdint.h>
#define RADIX_BITS 6
#define RADIX_SLOTS (1 << RADIX_BITS)
enum radix_tag : uint8_t {
RADIX_TAG_DIRTY,
RADIX_TAG_WRITEBACK,
RADIX_NUM_TAGS,
};
struct radix_node {
uint8_t shift;
uint8_t count;
uint8_t offset;
struct radix_node* parent;
uint64_t tags[RADIX_NUM_TAGS];
void* slots[RADIX_SLOTS];
};
struct radix_tree {
struct radix_node* root;
};
void radix_init(struct radix_tree* tree);
void radix_destroy(struct radix_tree* tree);
bool radix_empty(struct radix_tree* tree);
void* radix_lookup(struct radix_tree* tree, uint64_t index);
errno_t radix_insert(struct radix_tree* tree, uint64_t index, void* value);
void* radix_remove(struct radix_tree* tree, uint64_t index);
void** radix_slot(struct radix_tree* tree, uint64_t index);
void radix_tag_set(struct radix_tree* tree, uint64_t index, enum radix_tag tag);
void radix_tag_clear(struct radix_tree* tree, uint64_t index, enum radix_tag tag);
bool radix_tag_get(struct radix_tree* tree, uint64_t index, enum radix_tag tag);
bool radix_tagged(struct radix_tree* tree, enum radix_tag tag);
size_t radix_gang_lookup(struct radix_tree* tree, uint64_t first, size_t max, void** out, uint64_t* out_index);
size_t radix_gang_lookup_tag(
struct radix_tree* tree,
uint64_t first,
enum radix_tag tag,
size_t max,
void** out,
uint64_t* out_index
);
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include <zinnia/errno.h>
#include <zinnia/iovec.h>
#include <stddef.h>
#include <stdint.h>
struct ringbuf {
uint8_t* data;
size_t capacity;
size_t head;
size_t len;
};
errno_t ringbuf_init(struct ringbuf* rb, size_t capacity);
void ringbuf_fini(struct ringbuf* rb);
static inline size_t ringbuf_len(const struct ringbuf* rb) {
return rb->len;
}
static inline size_t ringbuf_space(const struct ringbuf* rb) {
return rb->capacity - rb->len;
}
static inline void ringbuf_clear(struct ringbuf* rb) {
rb->head = 0;
rb->len = 0;
}
size_t ringbuf_write(struct ringbuf* rb, const void* src, size_t len);
size_t ringbuf_read(struct ringbuf* rb, void* dst, size_t len);
errno_t ringbuf_write_iter(struct ringbuf* rb, iovec_iter_t* iter, size_t len, size_t* out_written);
errno_t ringbuf_read_iter(struct ringbuf* rb, iovec_iter_t* iter, size_t len, size_t* out_read);
+8
View File
@@ -42,6 +42,14 @@
(elm)->field.tql_prev = &(head)->tqh_first; \
} while (0)
#define tailq_insert_before(listelm, elm, field) \
do { \
(elm)->field.tql_prev = (listelm)->field.tql_prev; \
(elm)->field.tql_next = (listelm); \
*(listelm)->field.tql_prev = (elm); \
(listelm)->field.tql_prev = &(elm)->field.tql_next; \
} while (0)
#define tailq_remove(head, elm, field) \
do { \
if (((elm)->field.tql_next) != nullptr) \
+7 -7
View File
@@ -179,7 +179,7 @@ struct vfs_node_ops {
// VNODE_SYMLINK
struct {
// Reads the value of the symlink into `buf`.
errno_t (*read_link)(struct vfs_node* node, struct iovec_iter* iter, size_t* out_read);
errno_t (*read_link)(struct vfs_node* node, iovec_iter_t* iter, size_t* out_read);
} symlink;
// VNODE_DIR
@@ -264,8 +264,8 @@ struct vfs_node {
};
struct vfs_file_ops {
errno_t (*read)(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_read);
errno_t (*write)(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_written);
errno_t (*read)(struct vfs_file* file, iovec_iter_t* buffer, uint64_t offset, ssize_t* out_read);
errno_t (*write)(struct vfs_file* file, iovec_iter_t* buffer, uint64_t offset, ssize_t* out_written);
errno_t (*ioctl)(struct vfs_file* file, uintptr_t request, uintptr_t arg, uintptr_t* out_result);
errno_t (*sync)(struct vfs_file* file, bool data_only);
errno_t (*mmap)(
@@ -411,17 +411,17 @@ errno_t vfs_file_open(
// Reads from a `file` into a `buffer` from the current offset.
// Returns the actual bytes read in `out_read`.
errno_t vfs_file_read(struct vfs_file* file, struct iovec_iter* buffer, ssize_t* out_read);
errno_t vfs_file_read(struct vfs_file* file, iovec_iter_t* buffer, ssize_t* out_read);
// Same as `vfs_file_read`, but from a given offset.
errno_t vfs_file_pread(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_read);
errno_t vfs_file_pread(struct vfs_file* file, iovec_iter_t* buffer, uint64_t offset, ssize_t* out_read);
// Wites to a `file` from a `buffer` from the current offset.
// Returns the actual bytes written in `out_written`.
errno_t vfs_file_write(struct vfs_file* file, struct iovec_iter* buffer, ssize_t* out_written);
errno_t vfs_file_write(struct vfs_file* file, iovec_iter_t* buffer, ssize_t* out_written);
// Same as `vfs_file_write`, but from a given offset.
errno_t vfs_file_pwrite(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_written);
errno_t vfs_file_pwrite(struct vfs_file* file, iovec_iter_t* buffer, uint64_t offset, ssize_t* out_written);
// Performs a generic `request` on a `file`.
// Returns the result in `out_result`.