ringbuffer, guid, iovec stuff, and mmio
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
#include <zinnia/guid.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <string.h>
|
||||
|
||||
bool guid_is_zero(const struct guid* guid) {
|
||||
static const struct guid zero = {};
|
||||
return guid_equal(guid, &zero);
|
||||
}
|
||||
|
||||
bool guid_equal(const struct guid* a, const struct guid* b) {
|
||||
return memcmp(a, b, sizeof(struct guid)) == 0;
|
||||
}
|
||||
|
||||
void guid_format(const struct guid* guid, char out[static GUID_STR_LEN]) {
|
||||
ksnprintf(
|
||||
out,
|
||||
GUID_STR_LEN,
|
||||
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
guid->time_low,
|
||||
guid->time_mid,
|
||||
guid->time_hi_version,
|
||||
guid->clock_seq[0],
|
||||
guid->clock_seq[1],
|
||||
guid->node[0],
|
||||
guid->node[1],
|
||||
guid->node[2],
|
||||
guid->node[3],
|
||||
guid->node[4],
|
||||
guid->node[5]
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <zinnia/iovec.h>
|
||||
#include <zinnia/usercopy.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <string.h>
|
||||
|
||||
// Copies between `buf` and the iterator. If `to_iter` is set, data flows from `buf` into the iovecs.
|
||||
static errno_t iovec_iter_copy(iovec_iter_t* iter, void* buf, size_t len, bool to_iter, size_t* out_copied) {
|
||||
size_t progress = 0;
|
||||
errno_t status = 0;
|
||||
|
||||
while (progress < len && iter->current_idx < iter->iovecs_len) {
|
||||
iovec_t* vec = &iter->iovecs[iter->current_idx];
|
||||
size_t remaining = vec->len - iter->current_offset;
|
||||
if (remaining == 0) {
|
||||
iter->current_idx++;
|
||||
iter->current_offset = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t chunk = MIN(remaining, len - progress);
|
||||
void* ptr = (void*)(vec->base + iter->current_offset);
|
||||
uint8_t* kbuf = (uint8_t*)buf + progress;
|
||||
|
||||
if (iter->user) {
|
||||
bool ok = to_iter ? usercopy_write((__user void*)ptr, kbuf, chunk)
|
||||
: usercopy_read(kbuf, (const __user void*)ptr, chunk);
|
||||
if (!ok) {
|
||||
status = EFAULT;
|
||||
break;
|
||||
}
|
||||
} else if (to_iter) {
|
||||
memcpy(ptr, kbuf, chunk);
|
||||
} else {
|
||||
memcpy(kbuf, ptr, chunk);
|
||||
}
|
||||
|
||||
progress += chunk;
|
||||
iter->current_offset += chunk;
|
||||
iter->total_offset += chunk;
|
||||
}
|
||||
|
||||
if (out_copied)
|
||||
*out_copied = progress;
|
||||
return status;
|
||||
}
|
||||
|
||||
errno_t iovec_iter_copy_to(iovec_iter_t* iter, const void* src, size_t len, size_t* out_copied) {
|
||||
return iovec_iter_copy(iter, (void*)src, len, true, out_copied);
|
||||
}
|
||||
|
||||
errno_t iovec_iter_copy_from(iovec_iter_t* iter, void* dst, size_t len, size_t* out_copied) {
|
||||
return iovec_iter_copy(iter, dst, len, false, out_copied);
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
# Freestanding libc functions that are needed in the kernel
|
||||
|
||||
kernel_sources += files(
|
||||
'guid.c',
|
||||
'iovec.c',
|
||||
'radix.c',
|
||||
'ringbuf.c',
|
||||
'runtime.c',
|
||||
'stdlib.c',
|
||||
'string.c',
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/radix.h>
|
||||
|
||||
static bool covers(const struct radix_node* node, uint64_t index) {
|
||||
return node->shift + RADIX_BITS >= 64 || (index >> (node->shift + RADIX_BITS)) == 0;
|
||||
}
|
||||
|
||||
static size_t slot_of(const struct radix_node* node, uint64_t index) {
|
||||
return (index >> node->shift) & (RADIX_SLOTS - 1);
|
||||
}
|
||||
|
||||
static struct radix_node* node_alloc(uint8_t shift, struct radix_node* parent, uint8_t offset) {
|
||||
struct radix_node* node = mem_alloc(sizeof(struct radix_node), 0);
|
||||
if (!node)
|
||||
return nullptr;
|
||||
node->shift = shift;
|
||||
node->parent = parent;
|
||||
node->offset = offset;
|
||||
return node;
|
||||
}
|
||||
|
||||
static void free_subtree(struct radix_node* node) {
|
||||
if (node->shift > 0) {
|
||||
for (size_t i = 0; i < RADIX_SLOTS; i++) {
|
||||
if (node->slots[i])
|
||||
free_subtree(node->slots[i]);
|
||||
}
|
||||
}
|
||||
mem_free(node);
|
||||
}
|
||||
|
||||
void radix_init(struct radix_tree* tree) {
|
||||
tree->root = nullptr;
|
||||
}
|
||||
|
||||
void radix_destroy(struct radix_tree* tree) {
|
||||
if (tree->root)
|
||||
free_subtree(tree->root);
|
||||
tree->root = nullptr;
|
||||
}
|
||||
|
||||
bool radix_empty(struct radix_tree* tree) {
|
||||
return tree->root == nullptr;
|
||||
}
|
||||
|
||||
static struct radix_node* leaf_of(struct radix_tree* tree, uint64_t index) {
|
||||
struct radix_node* node = tree->root;
|
||||
if (!node || !covers(node, index))
|
||||
return nullptr;
|
||||
while (node->shift > 0) {
|
||||
node = node->slots[slot_of(node, index)];
|
||||
if (!node)
|
||||
return nullptr;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
void* radix_lookup(struct radix_tree* tree, uint64_t index) {
|
||||
struct radix_node* leaf = leaf_of(tree, index);
|
||||
return leaf ? leaf->slots[slot_of(leaf, index)] : nullptr;
|
||||
}
|
||||
|
||||
void** radix_slot(struct radix_tree* tree, uint64_t index) {
|
||||
struct radix_node* leaf = leaf_of(tree, index);
|
||||
return leaf ? &leaf->slots[slot_of(leaf, index)] : nullptr;
|
||||
}
|
||||
|
||||
static errno_t grow(struct radix_tree* tree, uint64_t index) {
|
||||
if (!tree->root) {
|
||||
uint8_t shift = 0;
|
||||
while (shift + RADIX_BITS < 64 && (index >> (shift + RADIX_BITS)) != 0)
|
||||
shift += RADIX_BITS;
|
||||
tree->root = node_alloc(shift, nullptr, 0);
|
||||
return tree->root ? 0 : ENOMEM;
|
||||
}
|
||||
|
||||
while (!covers(tree->root, index)) {
|
||||
struct radix_node* old = tree->root;
|
||||
struct radix_node* top = node_alloc(old->shift + RADIX_BITS, nullptr, 0);
|
||||
if (!top)
|
||||
return ENOMEM;
|
||||
top->slots[0] = old;
|
||||
top->count = 1;
|
||||
for (size_t t = 0; t < RADIX_NUM_TAGS; t++) {
|
||||
if (old->tags[t])
|
||||
top->tags[t] = 1;
|
||||
}
|
||||
old->parent = top;
|
||||
old->offset = 0;
|
||||
tree->root = top;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t radix_insert(struct radix_tree* tree, uint64_t index, void* value) {
|
||||
errno_t status = grow(tree, index);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
struct radix_node* node = tree->root;
|
||||
while (node->shift > 0) {
|
||||
const size_t slot = slot_of(node, index);
|
||||
struct radix_node* child = node->slots[slot];
|
||||
if (!child) {
|
||||
child = node_alloc(node->shift - RADIX_BITS, node, slot);
|
||||
if (!child)
|
||||
return ENOMEM;
|
||||
node->slots[slot] = child;
|
||||
node->count++;
|
||||
}
|
||||
node = child;
|
||||
}
|
||||
|
||||
const size_t slot = slot_of(node, index);
|
||||
if (node->slots[slot])
|
||||
return EEXIST;
|
||||
node->slots[slot] = value;
|
||||
node->count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clear_tag_upwards(struct radix_node* node, size_t slot, enum radix_tag tag) {
|
||||
while (node) {
|
||||
node->tags[tag] &= ~((uint64_t)1 << slot);
|
||||
if (node->tags[tag])
|
||||
return;
|
||||
slot = node->offset;
|
||||
node = node->parent;
|
||||
}
|
||||
}
|
||||
|
||||
void* radix_remove(struct radix_tree* tree, uint64_t index) {
|
||||
struct radix_node* node = leaf_of(tree, index);
|
||||
if (!node)
|
||||
return nullptr;
|
||||
|
||||
size_t slot = slot_of(node, index);
|
||||
void* value = node->slots[slot];
|
||||
if (!value)
|
||||
return nullptr;
|
||||
|
||||
for (size_t t = 0; t < RADIX_NUM_TAGS; t++) {
|
||||
if (node->tags[t] & ((uint64_t)1 << slot))
|
||||
clear_tag_upwards(node, slot, t);
|
||||
}
|
||||
|
||||
node->slots[slot] = nullptr;
|
||||
node->count--;
|
||||
while (node->count == 0) {
|
||||
struct radix_node* parent = node->parent;
|
||||
if (!parent) {
|
||||
tree->root = nullptr;
|
||||
mem_free(node);
|
||||
break;
|
||||
}
|
||||
slot = node->offset;
|
||||
parent->slots[slot] = nullptr;
|
||||
parent->count--;
|
||||
mem_free(node);
|
||||
node = parent;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void radix_tag_set(struct radix_tree* tree, uint64_t index, enum radix_tag tag) {
|
||||
struct radix_node* node = leaf_of(tree, index);
|
||||
if (!node || !node->slots[slot_of(node, index)])
|
||||
return;
|
||||
size_t slot = slot_of(node, index);
|
||||
while (node) {
|
||||
const uint64_t bit = (uint64_t)1 << slot;
|
||||
if (node->tags[tag] & bit)
|
||||
return;
|
||||
node->tags[tag] |= bit;
|
||||
slot = node->offset;
|
||||
node = node->parent;
|
||||
}
|
||||
}
|
||||
|
||||
void radix_tag_clear(struct radix_tree* tree, uint64_t index, enum radix_tag tag) {
|
||||
struct radix_node* node = leaf_of(tree, index);
|
||||
if (!node)
|
||||
return;
|
||||
const size_t slot = slot_of(node, index);
|
||||
if (node->tags[tag] & ((uint64_t)1 << slot))
|
||||
clear_tag_upwards(node, slot, tag);
|
||||
}
|
||||
|
||||
bool radix_tag_get(struct radix_tree* tree, uint64_t index, enum radix_tag tag) {
|
||||
struct radix_node* node = leaf_of(tree, index);
|
||||
return node && (node->tags[tag] & ((uint64_t)1 << slot_of(node, index)));
|
||||
}
|
||||
|
||||
bool radix_tagged(struct radix_tree* tree, enum radix_tag tag) {
|
||||
return tree->root && tree->root->tags[tag];
|
||||
}
|
||||
|
||||
struct gang {
|
||||
size_t max;
|
||||
size_t found;
|
||||
void** out;
|
||||
uint64_t* out_index;
|
||||
int tag;
|
||||
};
|
||||
|
||||
static void gang_walk(struct radix_node* node, uint64_t base, uint64_t first, struct gang* g) {
|
||||
size_t start = 0;
|
||||
if (first > base) {
|
||||
const uint64_t delta = (first - base) >> node->shift;
|
||||
if (delta >= RADIX_SLOTS)
|
||||
return;
|
||||
start = delta;
|
||||
}
|
||||
|
||||
for (size_t i = start; i < RADIX_SLOTS && g->found < g->max; i++) {
|
||||
if (!node->slots[i])
|
||||
continue;
|
||||
if (g->tag >= 0 && !(node->tags[g->tag] & ((uint64_t)1 << i)))
|
||||
continue;
|
||||
const uint64_t index = base + ((uint64_t)i << node->shift);
|
||||
if (node->shift == 0) {
|
||||
if (g->out)
|
||||
g->out[g->found] = node->slots[i];
|
||||
if (g->out_index)
|
||||
g->out_index[g->found] = index;
|
||||
g->found++;
|
||||
} else {
|
||||
gang_walk(node->slots[i], index, first > index ? first : index, g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t gang_lookup(struct radix_tree* tree, uint64_t first, int tag, size_t max, void** out, uint64_t* idx) {
|
||||
struct radix_node* root = tree->root;
|
||||
if (!root || max == 0 || !covers(root, first))
|
||||
return 0;
|
||||
struct gang g = {.max = max, .out = out, .out_index = idx, .tag = tag};
|
||||
gang_walk(root, 0, first, &g);
|
||||
return g.found;
|
||||
}
|
||||
|
||||
size_t radix_gang_lookup(struct radix_tree* tree, uint64_t first, size_t max, void** out, uint64_t* out_index) {
|
||||
return gang_lookup(tree, first, -1, max, out, 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
|
||||
) {
|
||||
return gang_lookup(tree, first, tag, max, out, out_index);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/ringbuf.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <string.h>
|
||||
|
||||
errno_t ringbuf_init(struct ringbuf* rb, size_t capacity) {
|
||||
rb->data = mem_alloc(capacity, ALLOC_NOZERO);
|
||||
if (!rb->data)
|
||||
return ENOMEM;
|
||||
rb->capacity = capacity;
|
||||
rb->head = 0;
|
||||
rb->len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ringbuf_fini(struct ringbuf* rb) {
|
||||
mem_free(rb->data);
|
||||
rb->data = nullptr;
|
||||
rb->capacity = 0;
|
||||
rb->len = 0;
|
||||
}
|
||||
|
||||
static size_t tail(const struct ringbuf* rb) {
|
||||
return (rb->head + rb->len) % rb->capacity;
|
||||
}
|
||||
|
||||
static size_t write_chunk(const struct ringbuf* rb, size_t len) {
|
||||
return MIN(len, MIN(ringbuf_space(rb), rb->capacity - tail(rb)));
|
||||
}
|
||||
|
||||
static size_t read_chunk(const struct ringbuf* rb, size_t len) {
|
||||
return MIN(len, MIN(rb->len, rb->capacity - rb->head));
|
||||
}
|
||||
|
||||
static void advance_read(struct ringbuf* rb, size_t len) {
|
||||
rb->head = (rb->head + len) % rb->capacity;
|
||||
rb->len -= len;
|
||||
if (rb->len == 0)
|
||||
rb->head = 0;
|
||||
}
|
||||
|
||||
size_t ringbuf_write(struct ringbuf* rb, const void* src, size_t len) {
|
||||
size_t done = 0;
|
||||
size_t chunk;
|
||||
while ((chunk = write_chunk(rb, len - done))) {
|
||||
memcpy(rb->data + tail(rb), (const uint8_t*)src + done, chunk);
|
||||
rb->len += chunk;
|
||||
done += chunk;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
size_t ringbuf_read(struct ringbuf* rb, void* dst, size_t len) {
|
||||
size_t done = 0;
|
||||
size_t chunk;
|
||||
while ((chunk = read_chunk(rb, len - done))) {
|
||||
memcpy((uint8_t*)dst + done, rb->data + rb->head, chunk);
|
||||
advance_read(rb, chunk);
|
||||
done += chunk;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
errno_t ringbuf_write_iter(struct ringbuf* rb, iovec_iter_t* iter, size_t len, size_t* out_written) {
|
||||
size_t done = 0;
|
||||
errno_t status = 0;
|
||||
size_t chunk;
|
||||
while (!status && (chunk = write_chunk(rb, len - done))) {
|
||||
size_t copied;
|
||||
status = iovec_iter_copy_from(iter, rb->data + tail(rb), chunk, &copied);
|
||||
rb->len += copied;
|
||||
done += copied;
|
||||
if (copied < chunk)
|
||||
break;
|
||||
}
|
||||
*out_written = done;
|
||||
return done ? 0 : status;
|
||||
}
|
||||
|
||||
errno_t ringbuf_read_iter(struct ringbuf* rb, iovec_iter_t* iter, size_t len, size_t* out_read) {
|
||||
size_t done = 0;
|
||||
errno_t status = 0;
|
||||
size_t chunk;
|
||||
while (!status && (chunk = read_chunk(rb, len - done))) {
|
||||
size_t copied;
|
||||
status = iovec_iter_copy_to(iter, rb->data + rb->head, chunk, &copied);
|
||||
advance_read(rb, copied);
|
||||
done += copied;
|
||||
if (copied < chunk)
|
||||
break;
|
||||
}
|
||||
*out_read = done;
|
||||
return done ? 0 : status;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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
|
||||
);
|
||||
@@ -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);
|
||||
@@ -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) \
|
||||
|
||||
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user