kernel: add header files

This commit is contained in:
2026-02-19 19:13:44 +00:00
parent f2e128c57e
commit 85006411bd
42 changed files with 3335 additions and 0 deletions

14
include/kernel/arg.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef KERNEL_ARG_H_
#define KERNEL_ARG_H_
#include <stdbool.h>
#include <mango/status.h>
#define CMDLINE_MAX 4096
extern kern_status_t parse_cmdline(const char *cmdline);
extern const char *arg_value(const char *arg_name);
extern bool arg_is_set(const char *arg_name);
#endif

35
include/kernel/bitmap.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef KERNEL_BITMAP_H_
#define KERNEL_BITMAP_H_
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define BITS_PER_WORD (8 * sizeof(unsigned long))
#define __DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#define BITMAP_WORDS(nbits) __DIV_ROUND_UP(nbits, BITS_PER_WORD)
#define BITMAP_NPOS ((unsigned int)-1)
#define DECLARE_BITMAP(name, nbits) unsigned long name[BITMAP_WORDS(nbits)]
extern void bitmap_zero(unsigned long *map, unsigned long nbits);
extern void bitmap_fill(unsigned long *map, unsigned long nbits);
extern void bitmap_set(unsigned long *map, unsigned long bit);
extern void bitmap_clear(unsigned long *map, unsigned long bit);
extern bool bitmap_check(unsigned long *map, unsigned long bit);
extern unsigned int bitmap_count_set(unsigned long *map, unsigned long nbits);
extern unsigned int bitmap_count_clear(unsigned long *map, unsigned long nbits);
extern unsigned int bitmap_highest_set(unsigned long *map, unsigned long nbits);
extern unsigned int bitmap_highest_clear(unsigned long *map, unsigned long nbits);
extern unsigned int bitmap_lowest_set(unsigned long *map, unsigned long nbits);
extern unsigned int bitmap_lowest_clear(unsigned long *map, unsigned long nbits);
#ifdef __cplusplus
}
#endif
#endif

38
include/kernel/bsp.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef KERNEL_BSP_H_
#define KERNEL_BSP_H_
#include <kernel/compiler.h>
#include <mango/status.h>
#include <kernel/types.h>
#include <stddef.h>
#include <stdint.h>
#define BSP_MAGIC 0xcafebabe
struct task;
struct bsp_trailer {
/* these fields are stored in big endian in the package itself */
uint32_t bsp_magic;
uint64_t bsp_fs_offset;
uint32_t bsp_fs_len;
uint64_t bsp_exec_offset;
uint32_t bsp_exec_len;
uint64_t bsp_text_faddr, bsp_text_vaddr, bsp_text_size;
uint64_t bsp_data_faddr, bsp_data_vaddr, bsp_data_size;
uint64_t bsp_exec_entry;
} __packed;
struct bsp {
/* the values in this struct are stored in host byte order */
struct bsp_trailer bsp_trailer;
struct vm_object *bsp_vmo;
};
extern void bsp_set_location(const struct boot_module *mod);
extern void bsp_get_location(struct boot_module *out);
extern kern_status_t bsp_load(struct bsp *bsp, const struct boot_module *mod);
extern kern_status_t bsp_launch_async(struct bsp *bsp, struct task *task);
#endif

475
include/kernel/btree.h Normal file
View File

@@ -0,0 +1,475 @@
/*
The Clear BSD License
Copyright (c) 2023 Max Wash
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the disclaimer
below) provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
*/
#ifndef KERNEL_BTREE_H_
#define KERNEL_BTREE_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* if your custom structure contains a struct btree_node (i.e. it can be part of
a btree), you can use this macro to convert a struct btree_node* to a
your_type*
@param t the name of your custom type (something that can be passed to
offsetof)
@param m the name of the struct btree_node member variable within your custom
type.
@param v the struct btree_node pointer that you wish to convert. if this is
NULL, NULL will be returned.
*/
#define BTREE_CONTAINER(t, m, v) \
((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0))
/* defines a simple node insertion function.
this function assumes that your nodes have simple integer keys that can be
compared with the usual operators.
EXAMPLE:
if you have a tree node type like this:
struct my_tree_node {
int key;
struct btree_node base;
}
You would use the following call to generate an insert function for a tree
with this node type:
BTREE_DEFINE_SIMPLE_INSERT(struct my_tree_node, base, key,
my_tree_node_insert);
Which would emit a function defined like:
static void my_tree_node_insert(struct btree *tree, struct my_tree_node
*node);
@param node_type your custom tree node type. usually a structure that
contains a struct btree_node member.
@param container_node_member the name of the struct btree_node member
variable within your custom type.
@param container_key_member the name of the key member variable within your
custom type.
@param function_name the name of the function to generate.
*/
#define BTREE_DEFINE_SIMPLE_INSERT( \
node_type, \
container_node_member, \
container_key_member, \
function_name) \
void function_name(struct btree *tree, node_type *node) \
{ \
if (!tree->b_root) { \
tree->b_root = &node->container_node_member; \
btree_insert_fixup( \
tree, \
&node->container_node_member); \
return; \
} \
\
struct btree_node *cur = tree->b_root; \
while (1) { \
node_type *cur_node = BTREE_CONTAINER( \
node_type, \
container_node_member, \
cur); \
struct btree_node *next = NULL; \
\
if (node->container_key_member \
> cur_node->container_key_member) { \
next = btree_right(cur); \
\
if (!next) { \
btree_put_right( \
cur, \
&node->container_node_member); \
break; \
} \
} else if ( \
node->container_key_member \
< cur_node->container_key_member) { \
next = btree_left(cur); \
\
if (!next) { \
btree_put_left( \
cur, \
&node->container_node_member); \
break; \
} \
} else { \
return; \
} \
\
cur = next; \
} \
\
btree_insert_fixup(tree, &node->container_node_member); \
}
/* defines a node insertion function.
this function should be used for trees with complex node keys that cannot be
directly compared. a comparator for your keys must be supplied.
EXAMPLE:
if you have a tree node type like this:
struct my_tree_node {
complex_key_t key;
struct btree_node base;
}
You would need to define a comparator function or macro with the following
signature:
int my_comparator(struct my_tree_node *a, struct my_tree_node *b);
Which implements the following:
return -1 if a < b
return 0 if a == b
return 1 if a > b
You would use the following call to generate an insert function for a tree
with this node type:
BTREE_DEFINE_INSERT(struct my_tree_node, base, key, my_tree_node_insert,
my_comparator);
Which would emit a function defined like:
static void my_tree_node_insert(struct btree *tree, struct my_tree_node
*node);
@param node_type your custom tree node type. usually a structure that
contains a struct btree_node member.
@param container_node_member the name of the struct btree_node member
variable within your custom type.
@param container_key_member the name of the key member variable within your
custom type.
@param function_name the name of the function to generate.
@param comparator the name of a comparator function or functional-macro that
conforms to the requirements listed above.
*/
#define BTREE_DEFINE_INSERT( \
node_type, \
container_node_member, \
container_key_member, \
function_name, \
comparator) \
void function_name(struct btree *tree, node_type *node) \
{ \
if (!tree->b_root) { \
tree->b_root = &node->container_node_member; \
btree_insert_fixup( \
tree, \
&node->container_node_member); \
return; \
} \
\
struct btree_node *cur = tree->b_root; \
while (1) { \
node_type *cur_node = BTREE_CONTAINER( \
node_type, \
container_node_member, \
cur); \
struct btree_node *next = NULL; \
int cmp = comparator(node, cur_node); \
\
if (cmp == 1) { \
next = btree_right(cur); \
\
if (!next) { \
btree_put_right( \
cur, \
&node->container_node_member); \
break; \
} \
} else if (cmp == -1) { \
next = btree_left(cur); \
\
if (!next) { \
btree_put_left( \
cur, \
&node->container_node_member); \
break; \
} \
} else { \
return; \
} \
\
cur = next; \
} \
\
btree_insert_fixup(tree, &node->container_node_member); \
}
/* defines a simple tree search function.
this function assumes that your nodes have simple integer keys that can be
compared with the usual operators.
EXAMPLE:
if you have a tree node type like this:
struct my_tree_node {
int key;
struct btree_node base;
}
You would use the following call to generate a search function for a tree
with this node type:
BTREE_DEFINE_SIMPLE_GET(struct my_tree_node, int, base, key,
my_tree_node_get);
Which would emit a function defined like:
static struct my_tree_node *my_tree_node_get(struct btree *tree, int key);
@param node_type your custom tree node type. usually a structure that
contains a struct btree_node member.
@param key_type the type name of the key embedded in your custom tree node
type. this type must be compatible with the builtin comparison operators.
@param container_node_member the name of the struct btree_node member
variable within your custom type.
@param container_key_member the name of the key member variable within your
custom type.
@param function_name the name of the function to generate.
*/
#define BTREE_DEFINE_SIMPLE_GET( \
node_type, \
key_type, \
container_node_member, \
container_key_member, \
function_name) \
node_type *function_name(struct btree *tree, key_type key) \
{ \
struct btree_node *cur = tree->b_root; \
while (cur) { \
node_type *cur_node = BTREE_CONTAINER( \
node_type, \
container_node_member, \
cur); \
if (key > cur_node->container_key_member) { \
cur = btree_right(cur); \
} else if (key < cur_node->container_key_member) { \
cur = btree_left(cur); \
} else { \
return cur_node; \
} \
} \
\
return NULL; \
}
/* perform an in-order traversal of a binary tree
If you have a tree defined like:
struct btree my_tree;
with nodes defined like:
struct my_tree_node {
int key;
struct btree_node base;
}
and you want to do something like:
foreach (struct my_tree_node *node : my_tree) { ... }
you should use this:
btree_foreach (struct my_tree_node, node, &my_tree, base) { ... }
@param iter_type the type name of the iterator variable. this should be the
tree's node type, and shouldn't be a pointer.
@param iter_name the name of the iterator variable.
@param tree_name a pointer to the tree to traverse.
@param node_member the name of the struct btree_node member variable within
the tree node type.
*/
#define btree_foreach(iter_type, iter_name, tree_name, node_member) \
for (iter_type *iter_name = BTREE_CONTAINER( \
iter_type, \
node_member, \
btree_first(tree_name)); \
iter_name; \
iter_name = BTREE_CONTAINER( \
iter_type, \
node_member, \
btree_next(&((iter_name)->node_member))))
/* perform an reverse in-order traversal of a binary tree
If you have a tree defined like:
struct btree my_tree;
with nodes defined like:
struct my_tree_node {
int key;
struct btree_node base;
}
and you want to do something like:
foreach (struct my_tree_node *node : reverse(my_tree)) { ... }
you should use this:
btree_foreach_r (struct my_tree_node, node, &my_tree, base) { ... }
@param iter_type the type name of the iterator variable. this should be the
tree's node type, and shouldn't be a pointer.
@param iter_name the name of the iterator variable.
@param tree_name a pointer to the tree to traverse.
@param node_member the name of the struct btree_node member variable within
the tree node type.
*/
#define btree_foreach_r(iter_type, iter_name, tree_name, node_member) \
for (iter_type *iter_name \
= BTREE_CONTAINER(iter_type, node_member, btree_last(tree_name)); \
iter_name; \
iter_name = BTREE_CONTAINER( \
iter_type, \
node_member, \
btree_prev(&((iter_name)->node_member))))
/* binary tree nodes. this *cannot* be used directly. you need to define a
custom node type that contains a member variable of type struct btree_node.
you would then use the supplied macros to define functions to manipulate your
custom binary tree.
*/
struct btree_node {
struct btree_node *b_parent, *b_left, *b_right;
unsigned short b_height;
};
/* binary tree. unlike struct btree_node, you can define variables of type
* struct btree. */
struct btree {
struct btree_node *b_root;
};
/* re-balance a binary tree after an insertion operation.
NOTE that, if you define an insertion function using BTREE_DEFINE_INSERT or
similar, this function will automatically called for you.
@param tree the tree to re-balance.
@param node the node that was just inserted into the tree.
*/
extern void btree_insert_fixup(struct btree *tree, struct btree_node *node);
/* delete a node from a binary tree and re-balance the tree afterwards.
@param tree the tree to delete from
@param node the node to delete.
*/
extern void btree_delete(struct btree *tree, struct btree_node *node);
/* get the first node in a binary tree.
this will be the node with the smallest key (i.e. the node that is
furthest-left from the root)
*/
extern struct btree_node *btree_first(struct btree *tree);
/* get the last node in a binary tree.
this will be the node with the largest key (i.e. the node that is
furthest-right from the root)
*/
extern struct btree_node *btree_last(struct btree *tree);
/* for any binary tree node, this function returns the node with the
* next-largest key value */
extern struct btree_node *btree_next(struct btree_node *node);
/* for any binary tree node, this function returns the node with the
* next-smallest key value */
extern struct btree_node *btree_prev(struct btree_node *node);
static inline bool btree_empty(const struct btree *tree)
{
return tree->b_root == NULL;
}
/* sets `child` as the immediate left-child of `parent` */
static inline void btree_put_left(
struct btree_node *parent,
struct btree_node *child)
{
parent->b_left = child;
child->b_parent = parent;
}
/* sets `child` as the immediate right-child of `parent` */
static inline void btree_put_right(
struct btree_node *parent,
struct btree_node *child)
{
parent->b_right = child;
child->b_parent = parent;
}
/* get the immediate left-child of `node` */
static inline struct btree_node *btree_left(struct btree_node *node)
{
return node->b_left;
}
/* get the immediate right-child of `node` */
static inline struct btree_node *btree_right(struct btree_node *node)
{
return node->b_right;
}
/* get the immediate parent of `node` */
static inline struct btree_node *btree_parent(struct btree_node *node)
{
return node->b_parent;
}
/* get the height of `node`.
the height of a node is defined as the length of the longest path
between the node and a leaf node.
this count includes the node itself, so the height of a leaf node will be 1.
*/
static inline unsigned short btree_height(struct btree_node *node)
{
return node->b_height;
}
#ifdef __cplusplus
}
#endif
#endif

53
include/kernel/channel.h Normal file
View File

@@ -0,0 +1,53 @@
#ifndef KERNEL_CHANNEL_H_
#define KERNEL_CHANNEL_H_
#include <kernel/object.h>
#include <kernel/sched.h>
struct kmsg;
struct channel {
struct object c_base;
unsigned int c_id;
struct waitqueue c_wq;
struct btree c_msg;
struct btree_node c_node;
};
extern kern_status_t channel_type_init(void);
extern struct channel *channel_create(void);
extern kern_status_t channel_enqueue_msg(
struct channel *channel,
struct kmsg *msg);
extern kern_status_t channel_recv_msg(
struct channel *channel,
struct msg *out_msg,
msgid_t *out_id,
unsigned long *irq_flags);
extern kern_status_t channel_reply_msg(
struct channel *channel,
msgid_t id,
const struct msg *resp,
unsigned long *irq_flags);
extern kern_status_t channel_read_msg(
struct channel *channel,
msgid_t msg,
size_t offset,
void *buf,
size_t len,
size_t *nr_read);
extern kern_status_t channel_write_msg(
struct channel *channel,
msgid_t msg,
size_t offset,
const void *buf,
size_t len,
size_t *nr_written);
DEFINE_OBJECT_LOCK_FUNCTION(channel, c_base)
#endif

27
include/kernel/clock.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef KERNEL_CLOCK_H_
#define KERNEL_CLOCK_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define HZ (clock_hz())
typedef uint64_t clock_ticks_t;
extern volatile clock_ticks_t clock_ticks;
extern clock_ticks_t clock_hz(void);
extern void clock_calibrate(clock_ticks_t hz);
extern void clock_advance(clock_ticks_t ticks);
extern void clock_wait(clock_ticks_t ticks);
#ifdef __cplusplus
}
#endif
#endif

41
include/kernel/compiler.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef KERNEL_COMPILER_H_
#define KERNEL_COMPILER_H_
#ifdef __cplusplus
template <typename T>
T read_once(const volatile T *ptr)
{
return *ptr;
}
template <typename T>
void write_once(volatile T *ptr, T value)
{
*ptr = value;
}
#else
#define READ_ONCE(p) (*((const volatile typeof(p) *)&(p)))
#define WRITE_ONCE(p, v) *(volatile typeof(p) *)&(p) = (v)
#endif
#undef __used
#define __used __attribute__((__used__))
#undef __noreturn
#define __noreturn __attribute__((__noreturn__))
#undef __packed
#define __packed __attribute__((__packed__))
#undef __section
#define __section(name) __attribute__((__section__(name)))
#undef __aligned
#define __aligned(x) __attribute__((__aligned__(x)))
#undef __weak
#define __weak __attribute__((__weak__))
#endif

56
include/kernel/console.h Normal file
View File

@@ -0,0 +1,56 @@
#ifndef KERNEL_CONSOLE_H_
#define KERNEL_CONSOLE_H_
/* The console system
Consoles are like simplified TTYs. Their purpose is to serve as an output
sink for messages printed using printk.
a struct console could be used to represent a serial port, UART port, or even
a text-based framebuffer display. Anything where the job of displaying
or sending text can be abstracted to a simple write() call.
A secondary purpose of consoles is to allow input. For example, a console
representing a serial port may allow both sending AND receiving over the
port.
*/
#include <kernel/queue.h>
#include <kernel/locks.h>
#include <mango/status.h>
#ifdef __cplusplus
extern "C" {
#endif
enum console_flags {
/* console is only used during the boot process. the console
will be automatically de-registered when the first
non-boot console is registered */
CON_BOOT = 0x01u,
};
struct console {
char c_name[16];
enum console_flags c_flags;
spin_lock_t c_lock;
void (*c_write)(struct console *, const char *, unsigned int);
int (*c_read)(struct console *, char *, unsigned int);
struct queue_entry c_list;
};
extern kern_status_t console_register(struct console *con);
extern kern_status_t console_unregister(struct console *con);
extern struct queue *get_consoles(unsigned long *flags);
extern void put_consoles(struct queue *consoles, unsigned long flags);
extern void console_write(struct console *con, const char *s, unsigned int len);
extern int console_read(struct console *con, char *s, unsigned int len);
#ifdef __cplusplus
}
#endif
#endif

65
include/kernel/cpu.h Normal file
View File

@@ -0,0 +1,65 @@
#ifndef KERNEL_CPU_H_
#define KERNEL_CPU_H_
#include <kernel/types.h>
#include <kernel/machine/cpu.h>
#include <stdint.h>
#include <kernel/sched.h>
#ifdef __cplusplus
extern "C" {
#endif
enum cpu_flags {
CPU_ONLINE = 0x01u,
};
struct cpu_data {
enum cpu_flags c_flags;
unsigned int c_id;
int c_preempt_count;
struct runqueue c_rq;
struct workqueue c_wq;
struct queue c_timers;
};
/* maximum number of processor cores that the kernel can support.
TODO move to build config option */
#define CPU_MAX 128
#define this_cpu() (ml_cpu_block_get_id(ml_this_cpu()))
extern struct cpu_data *get_this_cpu(void);
extern struct cpu_data *get_cpu(unsigned int id);
extern void put_cpu(struct cpu_data *cpu);
extern bool cpu_is_available(unsigned int cpu_id);
extern bool cpu_is_online(unsigned int cpu_id);
extern void cpu_set_available(unsigned int cpu_id);
extern void cpu_set_online(unsigned int cpu_id);
extern unsigned int cpu_nr_available(void);
extern unsigned int cpu_nr_online(void);
extern cycles_t get_cycles(void);
static inline cycles_t cycles_diff(cycles_t then, cycles_t now)
{
return now >= then ? now - then : (CYCLES_MAX - then) + now;
}
#define irq_enable() ml_int_enable()
#define irq_disable() ml_int_disable()
extern void preempt_disable(void);
extern void preempt_enable(void);
extern int preempt_count(void);
extern unsigned int cpu_get_highest_available(void);
#ifdef __cplusplus
}
#endif
#endif

49
include/kernel/fb.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef KERNEL_FB_H_
#define KERNEL_FB_H_
#include <stdint.h>
enum framebuffer_flags {
FB_MODE_RGB = 0x01u,
FB_MODE_VGATEXT = 0x02u,
FB_MODE_PALETTE = 0x04u,
};
struct framebuffer_bitfield {
uint32_t b_offset;
uint16_t b_length;
};
struct framebuffer_varinfo {
enum framebuffer_flags fb_flags;
uint32_t fb_xres;
uint32_t fb_yres;
uint32_t fb_bpp;
uint32_t fb_stride;
union {
struct {
uint32_t fb_xcells;
uint32_t fb_ycells;
};
struct {
struct framebuffer_bitfield fb_red;
struct framebuffer_bitfield fb_green;
struct framebuffer_bitfield fb_blue;
struct framebuffer_bitfield fb_alpha;
};
struct {
uintptr_t fb_palette_addr;
uint16_t fb_palette_nr_colours;
};
};
};
struct framebuffer_fixedinfo {
uint64_t fb_baseptr;
};
#endif

11
include/kernel/flags.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef KERNEL_FLAGS_H_
#define KERNEL_FLAGS_H_
#include <stdint.h>
typedef enum {
S_NORMAL = 0x00u,
S_NOBLOCK = 0x01u,
} mango_flags_t;
#endif

65
include/kernel/handle.h Normal file
View File

@@ -0,0 +1,65 @@
#ifndef KERNEL_HANDLE_H_
#define KERNEL_HANDLE_H_
#include <kernel/bitmap.h>
#include <mango/status.h>
#include <stddef.h>
#include <stdint.h>
/* subtract 32 bytes to account for the handle bitmap */
#define HANDLES_PER_TABLE ((4096 - 32) / sizeof(struct handle))
#define REFS_PER_TABLE ((4096 - 64) / sizeof(struct handle_table *))
typedef uint32_t kern_handle_t;
typedef uintptr_t handle_flags_t;
struct task;
struct object;
struct handle_list;
struct handle {
struct object *h_object;
handle_flags_t h_flags;
};
struct handle_table {
union {
struct {
/* bitmap tracks which bits in t_handle_list are
* allocated */
DECLARE_BITMAP(t_handle_map, HANDLES_PER_TABLE);
struct handle t_handle_list[HANDLES_PER_TABLE];
} t_handles;
struct {
/* bitmap tracks which sub-tables are fully-allocated */
DECLARE_BITMAP(t_subtable_map, REFS_PER_TABLE);
struct handle_table *t_subtable_list[REFS_PER_TABLE];
} t_subtables;
};
};
extern struct handle_table *handle_table_create(void);
extern void handle_table_destroy(struct handle_table *tab);
extern kern_status_t handle_table_alloc_handle(
struct handle_table *tab,
struct handle **out_slot,
kern_handle_t *out_handle);
extern kern_status_t handle_table_free_handle(
struct handle_table *tab,
kern_handle_t handle);
extern struct handle *handle_table_get_handle(
struct handle_table *tab,
kern_handle_t handle);
extern kern_status_t handle_list_transfer(
struct handle_table *dest,
struct handle_list *dest_list,
size_t dest_list_count,
struct handle_table *src,
const struct handle_list *src_list,
size_t src_list_count);
#endif

41
include/kernel/init.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef KERNEL_INIT_H_
#define KERNEL_INIT_H_
#include <kernel/compiler.h>
#include <kernel/machine/init.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef int (*initcall_t)(void);
#define INITLEVEL_EARLY 0
#define INITLEVEL_CORE 1
#define INITLEVEL_POSTCORE 2
#define INITLEVEL_ARCH 3
#define INITLEVEL_SUBSYS 4
#define INITLEVEL_ROOTFS 5
#define INITLEVEL_DEVICE 6
#define INITLEVEL_LATE 7
#define INITLEVEL_TESTS 8
#define early_initcall(fn) __define_initcall(fn, INITLEVEL_EARLY)
#define core_initcall(fn) __define_initcall(fn, INITLEVEL_CORE)
#define postcore_initcall(fn) __define_initcall(fn, INITLEVEL_POSTCORE)
#define arch_initcall(fn) __define_initcall(fn, INITLEVEL_ARCH)
#define subsys_initcall(fn) __define_initcall(fn, INITLEVEL_SUBSYS)
#define rootfs_initcall(fn) __define_initcall(fn, INITLEVEL_ROOTFS)
#define device_initcall(fn) __define_initcall(fn, INITLEVEL_DEVICE)
#define late_initcall(fn) __define_initcall(fn, INITLEVEL_LATE)
#define test_initcall(fn) __define_initcall(fn, INITLEVEL_TESTS)
extern void print_kernel_banner(void);
extern int do_initcalls(void);
extern int start_initlevel(int level);
#ifdef __cplusplus
}
#endif
#endif

184
include/kernel/input.h Normal file
View File

@@ -0,0 +1,184 @@
#ifndef KERNEL_INPUT_H_
#define KERNEL_INPUT_H_
#include <stdint.h>
#include <kernel/queue.h>
#include <mango/status.h>
enum input_event_hook_flags {
INPUT_HOOK_SQUASH_EVENT = 0x01u,
};
struct device;
enum input_event_type {
INPUT_TYPE_UNKNOWN = 0x00u,
INPUT_TYPE_KEY = 0x01u,
INPUT_TYPE_MOTION = 0x02u,
};
enum input_event_motion_type {
INPUT_MOTION_TYPE_MOUSE = 0x01u,
INPUT_MOTION_TYPE_SCROLL = 0x02u,
};
enum input_keycode {
KEY_UNKNOWN = 0x00u,
KEY_A = 0x01u,
KEY_B = 0x02u,
KEY_C = 0x03u,
KEY_D = 0x04u,
KEY_E = 0x05u,
KEY_F = 0x06u,
KEY_G = 0x07u,
KEY_H = 0x08u,
KEY_I = 0x09u,
KEY_J = 0x0Au,
KEY_K = 0x0Bu,
KEY_L = 0x0Cu,
KEY_M = 0x0Du,
KEY_N = 0x0Eu,
KEY_O = 0x0Fu,
KEY_P = 0x10u,
KEY_Q = 0x11u,
KEY_R = 0x12u,
KEY_S = 0x13u,
KEY_T = 0x14u,
KEY_U = 0x15u,
KEY_V = 0x16u,
KEY_W = 0x17u,
KEY_X = 0x18u,
KEY_Y = 0x19u,
KEY_Z = 0x1Au,
KEY_KEY_1 = 0x1Bu,
KEY_KEY_2 = 0x1Cu,
KEY_KEY_3 = 0x1Du,
KEY_KEY_4 = 0x1Eu,
KEY_KEY_5 = 0x1Fu,
KEY_KEY_6 = 0x20u,
KEY_KEY_7 = 0x21u,
KEY_KEY_8 = 0x22u,
KEY_KEY_9 = 0x23u,
KEY_KEY_0 = 0x24u,
KEY_ENTER = 0x25u,
KEY_ESCAPE = 0x26u,
KEY_BACKSPACE = 0x27u,
KEY_TAB = 0x28u,
KEY_SPACE = 0x29u,
KEY_MINUS = 0x2Au,
KEY_EQUALS = 0x2Bu,
KEY_LEFT_BRACE = 0x2Cu,
KEY_RIGHT_BRACE = 0x2Du,
KEY_BACKSLASH = 0x2Eu,
KEY_NON_US_HASH = 0x2Fu,
KEY_SEMICOLON = 0x30u,
KEY_APOSTROPHE = 0x31u,
KEY_GRAVE_ACCENT = 0x32u,
KEY_COMMA = 0x33u,
KEY_DOT = 0x34u,
KEY_SLASH = 0x35u,
KEY_CAPS_LOCK = 0x36u,
KEY_F1 = 0x37u,
KEY_F2 = 0x38u,
KEY_F3 = 0x39u,
KEY_F4 = 0x3Au,
KEY_F5 = 0x3Bu,
KEY_F6 = 0x3Cu,
KEY_F7 = 0x3Du,
KEY_F8 = 0x3Eu,
KEY_F9 = 0x3Fu,
KEY_F10 = 0x40u,
KEY_F11 = 0x41u,
KEY_F12 = 0x42u,
KEY_PRINT_SCREEN = 0x43u,
KEY_SCROLL_LOCK = 0x44u,
KEY_PAUSE = 0x45u,
KEY_INSERT = 0x46u,
KEY_HOME = 0x47u,
KEY_PAGE_UP = 0x48u,
KEY_DELETE = 0x49u,
KEY_END = 0x4Au,
KEY_PAGE_DOWN = 0x4Bu,
KEY_RIGHT = 0x4Cu,
KEY_LEFT = 0x4Du,
KEY_DOWN = 0x4Eu,
KEY_UP = 0x4Fu,
KEY_NUM_LOCK = 0x50u,
KEY_KEYPAD_SLASH = 0x51u,
KEY_KEYPAD_ASTERISK = 0x52u,
KEY_KEYPAD_MINUS = 0x53u,
KEY_KEYPAD_PLUS = 0x54u,
KEY_KEYPAD_ENTER = 0x55u,
KEY_KEYPAD_1 = 0x56u,
KEY_KEYPAD_2 = 0x57u,
KEY_KEYPAD_3 = 0x58u,
KEY_KEYPAD_4 = 0x59u,
KEY_KEYPAD_5 = 0x5Au,
KEY_KEYPAD_6 = 0x5Bu,
KEY_KEYPAD_7 = 0x5Cu,
KEY_KEYPAD_8 = 0x5Du,
KEY_KEYPAD_9 = 0x5Eu,
KEY_KEYPAD_0 = 0x5Fu,
KEY_KEYPAD_DOT = 0x60u,
KEY_NON_US_BACKSLASH = 0x61u,
KEY_KEYPAD_EQUALS = 0x62u,
KEY_MENU = 0x63u,
KEY_LEFT_CTRL = 0x64u,
KEY_LEFT_SHIFT = 0x65u,
KEY_LEFT_ALT = 0x66u,
KEY_LEFT_META = 0x67u,
KEY_RIGHT_CTRL = 0x68u,
KEY_RIGHT_SHIFT = 0x69u,
KEY_RIGHT_ALT = 0x6Au,
KEY_RIGHT_META = 0x6Bu,
KEY_MEDIA_MUTE = 0x6Cu,
KEY_MEDIA_VOLUME_INCREMENT = 0x6Du,
KEY_MEDIA_VOLUME_DECREMENT = 0x6Eu,
};
enum input_key_state {
INPUT_KEYSTATE_DOWN = 0x00u,
INPUT_KEYSTATE_UP = 0x01u,
};
enum input_button {
INPUT_BUTTON_MOUSE_LEFT = 0x00u,
INPUT_BUTTON_MOUSE_MIDDLE = 0x01u,
INPUT_BUTTON_MOUSE_RIGHT = 0x02u,
INPUT_BUTTON_MOUSE_BACK = 0x03u,
INPUT_BUTTON_MOUSE_FORWARD = 0x04u,
};
enum input_button_state {
INPUT_BUTTON_DOWN = 0x00u,
INPUT_BUTTON_UP = 0x01u,
};
struct input_event {
enum input_event_type ev_type;
union {
struct {
enum input_event_motion_type type;
int16_t movement_x;
int16_t movement_y;
} ev_motion;
struct {
enum input_button button;
enum input_button_state state;
} ev_button;
struct {
enum input_keycode key;
enum input_key_state state;
} ev_key;
};
};
struct input_event_hook {
void(*hook_callback)(struct device *, struct input_event *, enum input_event_hook_flags *, void *);
void *hook_arg;
struct queue_entry hook_head;
};
#endif

23
include/kernel/iovec.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef KERNEL_IOVEC_H_
#define KERNEL_IOVEC_H_
#include <mango/types.h>
#include <stddef.h>
struct iovec_iterator {
const struct iovec *it_vecs;
size_t it_nr_vecs;
size_t it_vec_ptr;
virt_addr_t it_base;
size_t it_len;
};
extern void iovec_iterator_begin(
struct iovec_iterator *it,
const struct iovec *vecs,
size_t nr_vecs);
extern void iovec_iterator_seek(struct iovec_iterator *it, size_t nr_bytes);
#endif

25
include/kernel/locks.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef KERNEL_LOCKS_H_
#define KERNEL_LOCKS_H_
#include <kernel/compiler.h>
#include <kernel/machine/hwlock.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef __aligned(8) ml_hwlock_t spin_lock_t;
#define SPIN_LOCK_INIT ML_HWLOCK_INIT
#define spin_lock(lck) ml_hwlock_lock(lck);
#define spin_unlock(lck) ml_hwlock_unlock(lck);
#define spin_lock_irqsave(lck, flags) ml_hwlock_lock_irqsave(lck, flags);
#define spin_unlock_irqrestore(lck, flags) ml_hwlock_unlock_irqrestore(lck, flags);
#ifdef __cplusplus
}
#endif
#endif

345
include/kernel/memblock.h Normal file
View File

@@ -0,0 +1,345 @@
/*
The Clear BSD License
Copyright (c) 2023 Max Wash
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the disclaimer
below) provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
*/
#ifndef KERNEL_MEMBLOCK_H_
#define KERNEL_MEMBLOCK_H_
#include <limits.h>
#include <kernel/types.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MEMBLOCK_INIT_MEMORY_REGION_COUNT 128
#define MEMBLOCK_INIT_RESERVED_REGION_COUNT 128
#define __for_each_mem_range(i, type_a, type_b, p_start, p_end) \
for ((i)->__idx = 0, \
__next_memory_region(i, type_a, type_b, p_start, p_end); \
(i)->__idx != ULLONG_MAX; \
__next_memory_region(i, type_a, type_b, p_start, p_end))
/* iterate through all memory regions known to memblock.
this consists of all regions that have been registered
with memblock using memblock_add().
this iteration can be optionally constrained to a given region.
@param i the iterator. this should be a pointer of type struct memblock_iter.
for each iteration, this structure will be filled with details about
the current memory region.
@param p_start the lower bound of the memory region to iterate through.
if you don't want to use a lower bound, pass 0.
@param p_end the upper bound of the memory region to iterate through.
if you don't want to use an upper bound, pass UINTPTR_MAX.
EXAMPLE: to iterate through all memory regions (with no bounds):
struct memblock_iter it;
for_each_mem_region (&it, 0x0, UINTPTR_MAX) { ... }
EXAMPLE: to iterate through all memory regions between physical
addresses 0x40000 and 0x80000:
struct memblock_iter it;
for_each_mem_region (&it, 0x40000, 0x80000) { ... }
*/
#define for_each_mem_range(i, p_start, p_end) \
__for_each_mem_range(i, &memblock.memory, NULL, p_start, p_end)
/* iterate through all memory regions reserved using memblock.
this consists of all regions that have been registered
with memblock using memblock_reserve().
this iteration can be optionally constrained to a given region.
@param i the iterator. this should be a pointer of type struct memblock_iter.
for each iteration, this structure will be filled with details about
the current memory region.
@param p_start the lower bound of the memory region to iterate through.
if you don't want to use a lower bound, pass 0.
@param p_end the upper bound of the memory region to iterate through.
if you don't want to use an upper bound, pass UINTPTR_MAX.
EXAMPLE: to iterate through all reserved memory regions (with no bounds):
struct memblock_iter it;
for_each_reserved_mem_region (&it, 0x0, UINTPTR_MAX) { ... }
EXAMPLE: to iterate through all reserved memory regions between physical
addresses 0x40000 and 0x80000:
struct memblock_iter it;
for_each_reserved_mem_region (&it, 0x40000, 0x80000) { ... }
*/
#define for_each_reserved_mem_range(i, p_start, p_end) \
__for_each_mem_range(i, &memblock.reserved, NULL, p_start, p_end)
/* iterate through all memory regions known by memblock to be free.
this consists of all regions BETWEEN those regions that have been
registered using memblock_reserve(), bounded within the memory
regions added using memblock_add().
this iteration can be optionally constrained to a given region.
@param i the iterator. this should be a pointer of type struct memblock_iter.
for each iteration, this structure will be filled with details about
the current memory region.
@param p_start the lower bound of the memory region to iterate through.
if you don't want to use a lower bound, pass 0.
@param p_end the upper bound of the memory region to iterate through.
if you don't want to use an upper bound, pass UINTPTR_MAX.
EXAMPLE: if you have added the following memory regions to
memblock using memblock_add():
- 0x00000 -> 0x05fff
- 0x08000 -> 0x1ffff
...and you have reserved the following memory regions using
memblock_reserve():
- 0x01000 -> 0x04fff
- 0x09000 -> 0x0ffff
the following call:
struct memblock_iter it;
for_each_free_mem_range (&it, 0x0, UINTPTR_MAX) { ... }
would iterate through the following sequence of free memory ranges:
- 0x00000 -> 0x00fff
- 0x05000 -> 0x05fff
- 0x08000 -> 0x08fff
- 0x10000 -> 0x1ffff
*/
#define for_each_free_mem_range(i, p_start, p_end) \
__for_each_mem_range( \
i, \
&memblock.memory, \
&memblock.reserved, \
p_start, \
p_end)
typedef uint64_t memblock_index_t;
enum memblock_region_status {
/* Used in memblock.memory regions, indicates that the memory region
* exists */
MEMBLOCK_MEMORY = 0,
/* Used in memblock.reserved regions, indicates that the memory region
* was reserved by a call to memblock_alloc() */
MEMBLOCK_ALLOC,
/* Used in memblock.reserved regions, indicates that the memory region
* was reserved by a call to memblock_reserve() */
MEMBLOCK_RESERVED,
};
struct memblock_region {
/* the status of the memory region (free, reserved, allocated, etc) */
enum memblock_region_status status;
/* the address of the first byte that makes up the region */
phys_addr_t base;
/* the address of the last byte that makes up the region */
phys_addr_t limit;
};
/* buffer of memblock regions, all of which are the same type
(memory, reserved, etc) */
struct memblock_type {
struct memblock_region *regions;
unsigned int count;
unsigned int max;
const char *name;
};
struct memblock {
/* bounds of the memory region that can be used by memblock_alloc()
both of these are virtual addresses */
uintptr_t m_alloc_start, m_alloc_end;
/* memblock assumes that all memory in the alloc zone is contiguously
mapped (if paging is enabled). m_voffset is the offset that needs to
be added to a given physical address to get the corresponding virtual
address */
uintptr_t m_voffset;
struct memblock_type memory;
struct memblock_type reserved;
};
struct memblock_iter {
memblock_index_t __idx;
phys_addr_t it_base;
phys_addr_t it_limit;
enum memblock_region_status it_status;
};
/* global memblock state. */
extern struct memblock memblock;
extern int __next_mem_range(struct memblock_iter *it);
/* initialise the global memblock state.
this function must be called before any other memblock functions can be used.
this function sets the bounds of the heap area. memory allocation requests
using memblock_alloc() will be constrained to this zone.
memblock assumes that all physical memory in the system is mapped to
an area in virtual memory, such that converting a physical address to
a valid virtual address can be done by simply applying an offset.
@param alloc_start the virtual address of the start of the heap area.
@param alloc_end the virtual address of the end of the heap area.
@param voffset the offset between the physical address of a given page and
its corresponding virtual address.
*/
extern int memblock_init(
uintptr_t alloc_start,
uintptr_t alloc_end,
uintptr_t voffset);
/* add a region of memory to memblock.
this function is used to define regions of memory that are accessible, but
says nothing about the STATE of the given memory.
all memory is free by default. once a region of memory is added,
memblock_reserve() can be used to mark the memory as reserved.
@param base the physical address of the start of the memory region to add.
@oaram size the size of the memory region to add in bytes.
*/
extern int memblock_add(phys_addr_t base, size_t size);
/* mark a region of memory as reserved.
this function can only operate on regions of memory that have been previously
registered with memblock using memblock_add().
reserved memory will not be used by memblock_alloc(), and will remain
reserved when the vm_page memory map is initialised.
@param base the physical address of the start of the memory region to
reserve.
@oaram size the size of the memory region to reserve in bytes.
*/
extern int memblock_reserve(phys_addr_t base, size_t size);
/* allocate a block of memory, returning a virtual address.
this function selects the first available region of memory that satisfies
the requested allocation size, marks `size` bytes of this region as reserved,
and returns the virtual address of the region.
when looking for a suitable region of memory, this function searches the
intersection of the following memory zones:
- the regions of memory added with memblock_alloc().
- the region of memory specified as the heap bounds during the call
to memblock_init().
and excludes the following regions:
- the regions of memory marked as reserved by memblock_reserve() and
previous calls to memblock_alloc()
@param size the size of the buffer to allocate in bytes.
@param align the alignment to use. for example, an alignment of 4096
will result in the returned pointer being a multiple
of 4096. this must be a power of 2.
*/
extern void *memblock_alloc(size_t size, phys_addr_t align);
/* allocate a block of memory, returning a physical address.
this function selects the first available region of memory that satisfies
the requested allocation size, marks `size` bytes of this region as reserved,
and returns the virtual address of the region.
when looking for a suitable region of memory, this function searches the
intersection of the following memory zones:
- the regions of memory added with memblock_alloc().
- the region of memory specified as the heap bounds during the call
to memblock_init().
and excludes the following regions:
- the regions of memory marked as reserved by memblock_reserve() and
previous calls to memblock_alloc()
@param size the size of the buffer to allocate in bytes.
@param align the alignment to use. for example, an alignment of 4096
will result in the returned pointer being a multiple
of 4096. this must be a power of 2.
*/
extern phys_addr_t memblock_alloc_phys(size_t size, phys_addr_t align);
/* free a block of memory using its virtual address.
due to limitations in memblock (as it is meant to be a simple,
early-boot allocator), you must specify the size of the memory
region you intend to free.
@param addr the virtual address of the region to free.
@param size the size of the region to free in bytes.
*/
extern int memblock_free(void *addr, size_t size);
/* free a block of memory using its physical address.
due to limitations in memblock (as it is meant to be a simple,
early-boot allocator), you must specify the size of the memory
region you intend to free.
@param addr the physical address of the region to free.
@param size the size of the region to free in bytes.
*/
extern int memblock_free_phys(phys_addr_t addr, size_t size);
/* convert a virtual pointer returned by memblock
to a physical address.
@param p the pointer to convert.
*/
extern phys_addr_t memblock_virt_to_phys(const void *p);
/* convert a physical address returned by memblock
to a virtual pointer.
@param p the address to convert.
*/
extern void *memblock_phys_to_virt(phys_addr_t p);
extern void __next_memory_region(
struct memblock_iter *it,
struct memblock_type *type_a,
struct memblock_type *type_b,
phys_addr_t start,
phys_addr_t end);
#ifdef __cplusplus
}
#endif
#endif

30
include/kernel/msg.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef KERNEL_MSG_H_
#define KERNEL_MSG_H_
#include <kernel/btree.h>
#include <kernel/locks.h>
#include <mango/status.h>
#include <mango/types.h>
struct port;
struct thread;
enum kmsg_status {
KMSG_WAIT_RECEIVE,
KMSG_WAIT_REPLY,
KMSG_REPLY_SENT,
};
struct kmsg {
spin_lock_t msg_lock;
enum kmsg_status msg_status;
struct btree_node msg_node;
msgid_t msg_id;
kern_status_t msg_result;
struct port *msg_sender_port;
struct thread *msg_sender_thread;
const struct msg *msg_req;
struct msg *msg_resp;
};
#endif

95
include/kernel/object.h Normal file
View File

@@ -0,0 +1,95 @@
#ifndef KERNEL_OBJECT_H_
#define KERNEL_OBJECT_H_
#include <kernel/flags.h>
#include <kernel/locks.h>
#include <mango/status.h>
#include <kernel/vm.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#define DEFINE_OBJECT_LOCK_FUNCTION(object_name, base) \
static inline void object_name##_lock(struct object_name *p) \
{ \
object_lock(&p->base); \
} \
static inline void object_name##_unlock(struct object_name *p) \
{ \
object_unlock(&p->base); \
} \
static inline void object_name##_lock_irqsave( \
struct object_name *p, \
unsigned long *flags) \
{ \
object_lock_irqsave(&p->base, flags); \
} \
static inline void object_name##_unlock_irqrestore( \
struct object_name *p, \
unsigned long flags) \
{ \
object_unlock_irqrestore(&p->base, flags); \
}
#define OBJECT_MAGIC 0xBADDCAFE
#define OBJECT_NAME_MAX 64
#define OBJECT_PATH_MAX 256
#define OBJECT_CAST(to_type, to_type_member, p) \
((to_type *)((uintptr_t)p) - offsetof(to_type, to_type_member))
#define OBJECT_C_CAST(c_type, c_type_member, obj_type, objp) \
OBJECT_IS_TYPE(objp, obj_type) \
? OBJECT_CAST(c_type, c_type_member, (objp)) : NULL
#define OBJECT_IS_TYPE(obj, type_ptr) ((obj)->ob_type == (type_ptr))
struct object;
struct object_attrib;
enum object_type_flags {
OBJTYPE_INIT = 0x01u,
};
struct object_ops {
kern_status_t (*destroy)(struct object *obj);
};
struct object_type {
enum object_type_flags ob_flags;
char ob_name[32];
unsigned int ob_size;
unsigned int ob_header_offset;
struct vm_cache ob_cache;
struct queue_entry ob_list;
struct object_ops ob_ops;
};
struct object {
uint32_t ob_magic;
struct object_type *ob_type;
spin_lock_t ob_lock;
unsigned int ob_refcount;
unsigned int ob_handles;
struct queue_entry ob_list;
} __aligned(sizeof(long));
extern kern_status_t object_bootstrap(void);
extern kern_status_t object_type_register(struct object_type *p);
extern kern_status_t object_type_unregister(struct object_type *p);
extern struct object *object_create(struct object_type *type);
extern struct object *object_ref(struct object *obj);
extern void object_unref(struct object *obj);
extern void object_add_handle(struct object *obj);
extern void object_remove_handle(struct object *obj);
extern void object_lock(struct object *obj);
extern void object_unlock(struct object *obj);
extern void object_lock_irqsave(struct object *obj, unsigned long *flags);
extern void object_unlock_irqrestore(struct object *obj, unsigned long flags);
#ifdef __cplusplus
}
#endif
#endif

13
include/kernel/panic.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef KERNEL_PANIC_H_
#define KERNEL_PANIC_H_
#include <kernel/compiler.h>
struct ml_cpu_context;
#define panic(...) panic_irq(NULL, __VA_ARGS__)
extern void __noreturn
panic_irq(struct ml_cpu_context *ctx, const char *fmt, ...);
#endif

36
include/kernel/percpu.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef KERNEL_PERCPU_H_
#define KERNEL_PERCPU_H_
#include <mango/status.h>
#include <kernel/compiler.h>
#include <kernel/sched.h>
#ifdef __cplusplus
extern "C" {
#endif
#define DEFINE_PERCPU_VAR(type, name) \
__section(".data.percpu") type name
#define percpu_get(var) \
__extension__({ \
preempt_disable(); \
__percpu_get(this_cpu(), var); \
})
#define percpu_get_from(cpu, var) \
__extension__({ \
preempt_disable(); \
__percpu_get(cpu, var); \
})
#define percpu_put(var) preempt_enable();
extern kern_status_t init_per_cpu_areas(void);
extern void *__percpu_get(unsigned int cpu, void *var);
#ifdef __cplusplus
}
#endif
#endif

81
include/kernel/pmap.h Normal file
View File

@@ -0,0 +1,81 @@
#ifndef KERNEL_PMAP_H_
#define KERNEL_PMAP_H_
/* all the functions declared in this file are defined in arch/xyz/pmap.c */
#include <kernel/machine/pmap.h>
#include <kernel/vm.h>
#include <mango/status.h>
#include <stddef.h>
#define PMAP_INVALID ML_PMAP_INVALID
#define PFN(x) ((x) >> VM_PAGE_SHIFT)
#ifdef __cplusplus
extern "C" {
#endif
typedef ml_pmap_t pmap_t;
typedef ml_pfn_t pfn_t;
enum pmap_fault_flags {
/* if set, the faulting page is present, and the fault is
* protection-related.
* if clear, the faulting page is missing, and the
* fault is due to the missing page.
*/
PMAP_FAULT_PRESENT = 0x01u,
/* if set, the faulting page was accessed from user mode.
* if clear, the faulting page was accessed from kernel mode.
*/
PMAP_FAULT_USER = 0x02u,
/* if set, the fault was caused by a write operation.
* if clear, the faulting page was caused by a read operation.
*/
PMAP_FAULT_WRITE = 0x04u,
/* if set, the fault was caused while fetching an instruction from the
* faulting page.
*/
PMAP_FAULT_IFETCH = 0x08u,
/* if set, the fault was caused by misconfigured page tables */
PMAP_FAULT_BADCFG = 0x10u,
};
enum pmap_flags {
PMAP_NORMAL = 0x00u,
PMAP_HUGEPAGE = 0x01u,
};
extern void pmap_bootstrap(void);
extern pmap_t get_kernel_pmap(void);
extern pmap_t pmap_create(void);
extern void pmap_destroy(pmap_t pmap);
extern void pmap_switch(pmap_t pmap);
extern kern_status_t pmap_handle_fault(
virt_addr_t fault_addr,
enum pmap_fault_flags flags);
extern kern_status_t pmap_add(
pmap_t pmap,
virt_addr_t p,
pfn_t pfn,
vm_prot_t prot,
enum pmap_flags flags);
extern kern_status_t pmap_add_block(
pmap_t pmap,
virt_addr_t p,
pfn_t pfn,
size_t len,
vm_prot_t prot,
enum pmap_flags flags);
extern kern_status_t pmap_remove(pmap_t pmap, virt_addr_t p);
extern kern_status_t pmap_remove_range(pmap_t pmap, virt_addr_t p, size_t len);
#ifdef __cplusplus
}
#endif
#endif

39
include/kernel/port.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef KERNEL_PORT_H_
#define KERNEL_PORT_H_
#include <kernel/object.h>
#include <kernel/sched.h>
enum port_status {
/* port is not connected */
PORT_OFFLINE = 0,
/* port is connected and ready to send messages */
PORT_READY,
/* port has sent a message, and is waiting for the remote to receive it
*/
PORT_SEND_BLOCKED,
/* port has sent a message, and the remote has received it. waiting for
* the remote to reply. */
PORT_REPLY_BLOCKED,
};
struct port {
struct object p_base;
enum port_status p_status;
struct channel *p_remote;
};
extern kern_status_t port_type_init(void);
extern struct port *port_cast(struct object *obj);
extern struct port *port_create(void);
extern kern_status_t port_connect(struct port *port, struct channel *remote);
extern kern_status_t port_send_msg(
struct port *port,
const struct msg *req,
struct msg *resp);
DEFINE_OBJECT_LOCK_FUNCTION(port, p_base)
#endif

25
include/kernel/printk.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef KERNEL_PRINTK_H_
#define KERNEL_PRINTK_H_
#include <kernel/console.h>
#undef TRACE
#ifdef __cplusplus
extern "C" {
#endif
#ifdef TRACE
#define tracek(...) printk(__VA_ARGS__)
#else
#define tracek(...)
#endif
extern void early_printk_init(struct console *con);
extern int printk(const char *format, ...);
#ifdef __cplusplus
}
#endif
#endif

62
include/kernel/queue.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef KERNEL_QUEUE_H_
#define KERNEL_QUEUE_H_
#include <kernel/libc/string.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define QUEUE_CONTAINER(t, m, v) ((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0))
#define QUEUE_INIT ((struct queue){ .q_first = NULL, .q_last = NULL })
#define QUEUE_ENTRY_INIT ((struct queue_entry){ .qe_next = NULL, .qe_prev = NULL })
#define queue_foreach(iter_type, iter_name, queue_name, node_member) \
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER(iter_type, node_member, queue_first(queue_name)); \
iter_name; \
iter_name = (iter_type *)QUEUE_CONTAINER(iter_type, node_member, queue_next(&((iter_name)->node_member))))
#define queue_foreach_r(iter_type, iter_name, queue_name, node_member) \
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER(iter_type, node_member, queue_last(queue_name)); \
iter_name; \
iter_name = (iter_type *)QUEUE_CONTAINER(iter_type, node_member, queue_prev(&((iter_name)->node_member))))
struct queue_entry {
struct queue_entry *qe_next;
struct queue_entry *qe_prev;
};
struct queue {
struct queue_entry *q_first;
struct queue_entry *q_last;
};
static inline void queue_init(struct queue *q) { memset(q, 0x00, sizeof *q); }
static inline bool queue_empty(struct queue *q) { return q->q_first == NULL; }
static inline struct queue_entry *queue_first(struct queue *q) { return q->q_first; }
static inline struct queue_entry *queue_last(struct queue *q) { return q->q_last; }
static inline struct queue_entry *queue_next(struct queue_entry *entry) { return entry->qe_next; }
static inline struct queue_entry *queue_prev(struct queue_entry *entry) { return entry->qe_prev; }
extern size_t queue_length(struct queue *q);
extern void queue_insert_before(struct queue *q, struct queue_entry *entry, struct queue_entry *before);
extern void queue_insert_after(struct queue *q, struct queue_entry *entry, struct queue_entry *after);
extern void queue_push_front(struct queue *q, struct queue_entry *entry);
extern void queue_push_back(struct queue *q, struct queue_entry *entry);
extern struct queue_entry *queue_pop_front(struct queue *q);
extern struct queue_entry *queue_pop_back(struct queue *q);
extern void queue_delete(struct queue *q, struct queue_entry *entry);
extern void queue_delete_all(struct queue *q);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,34 @@
#ifndef KERNEL_RINGBUFFER_H_
#define KERNEL_RINGBUFFER_H_
#include <kernel/locks.h>
#include <kernel/sched.h>
struct ringbuffer {
unsigned char *r_buffer;
size_t r_write_ptr;
size_t r_read_ptr;
size_t r_size;
spin_lock_t r_lock;
struct waitqueue r_wait_readers;
struct waitqueue r_wait_writers;
};
extern struct ringbuffer *ringbuffer_create(size_t size);
extern void ringbuffer_destroy(struct ringbuffer *buf);
extern kern_status_t ringbuffer_init(struct ringbuffer *buf, size_t size);
extern kern_status_t ringbuffer_deinit(struct ringbuffer *buf);
extern size_t ringbuffer_unread(struct ringbuffer *buf);
extern size_t ringbuffer_avail(struct ringbuffer *buf);
extern size_t ringbuffer_read(struct ringbuffer *buf, size_t size, void *buffer, mango_flags_t flags);
extern size_t ringbuffer_write(struct ringbuffer *buf, size_t size, const void *buffer, mango_flags_t flags);
/* TODO */
//extern size_t ringbuffer_peek(struct ringbuffer *buf, size_t at, size_t size, void *buffer);
//extern size_t ringbuffer_skip(struct ringbuffer *buf, size_t count);
extern int ringbuffer_write_would_block(struct ringbuffer *buf);
#endif

277
include/kernel/sched.h Normal file
View File

@@ -0,0 +1,277 @@
#ifndef KERNEL_SCHED_H_
#define KERNEL_SCHED_H_
#include <kernel/btree.h>
#include <kernel/handle.h>
#include <kernel/locks.h>
#include <kernel/msg.h>
#include <kernel/object.h>
#include <kernel/pmap.h>
#include <kernel/queue.h>
#include <mango/status.h>
#define TASK_NAME_MAX 64
#define PRIO_MAX 32
#define PID_MAX 99999
#define THREAD_KSTACK_ORDER VM_PAGE_4K
#define THREAD_MAX 65536
#define wait_event(wq, cond) \
({ \
struct thread *self = current_thread(); \
struct wait_item waiter; \
wait_item_init(&waiter, self); \
for (;;) { \
thread_wait_begin(&waiter, wq); \
if (cond) { \
break; \
} \
schedule(SCHED_NORMAL); \
} \
thread_wait_end(&waiter, wq); \
})
#ifdef __cplusplus
extern "C" {
#endif
struct channel;
struct runqueue;
struct work_item;
enum task_state {
TASK_RUNNING,
TASK_STOPPED,
};
enum thread_state {
THREAD_READY = 1,
THREAD_SLEEPING = 2,
THREAD_STOPPED = 3,
};
enum thread_flags {
THREAD_F_NEED_RESCHED = 0x01u,
THREAD_F_NO_PREEMPT = 0x02u,
};
enum sched_priority {
PRIO_IDLE = 4,
PRIO_SUBNORMAL = 6,
PRIO_NORMAL = 10,
PRIO_SUPERNORMAL = 14,
PRIO_HIGH = 18,
PRIO_REALTIME = 24,
};
enum sched_mode {
/* used when calling from non-interrupt context.
threads that aren't in state THREAD_READY are
removed from the runqueue. */
SCHED_NORMAL = 0,
/* used when calling from interrupt context.
threads that aren't in state THREAD_READY are
still added to the runqueue. */
SCHED_IRQ = 1,
};
struct task {
struct object t_base;
struct task *t_parent;
long t_id;
enum task_state t_state;
char t_name[TASK_NAME_MAX];
pmap_t t_pmap;
struct vm_region *t_address_space;
struct handle_table *t_handles;
struct btree b_channels;
struct btree_node t_tasklist;
struct queue_entry t_child_entry;
size_t t_next_thread_id;
struct queue t_threads;
struct queue t_children;
};
struct thread {
struct object thr_base;
enum thread_state tr_state;
enum thread_flags tr_flags;
struct task *tr_parent;
unsigned int tr_id;
unsigned int tr_prio;
cycles_t tr_charge_period_start;
cycles_t tr_quantum_cycles, tr_quantum_target;
cycles_t tr_total_cycles;
virt_addr_t tr_ip, tr_sp, tr_bp;
virt_addr_t tr_cpu_user_sp, tr_cpu_kernel_sp;
struct runqueue *tr_rq;
struct kmsg tr_msg;
struct queue_entry tr_parent_entry;
struct queue_entry tr_rqentry;
struct vm_page *tr_kstack;
struct vm_object *tr_ustack;
};
struct runqueue {
struct queue rq_queues[PRIO_MAX];
uint32_t rq_readybits;
spin_lock_t rq_lock;
unsigned int rq_nthreads;
struct thread *rq_cur, *rq_idle;
};
struct timer {
struct queue_entry t_entry;
struct cpu_data *t_cpu;
struct thread *t_owner;
unsigned long t_expiry;
void (*t_callback)(struct timer *);
};
struct wait_item {
struct thread *w_thread;
struct queue_entry w_entry;
};
struct waitqueue {
struct queue wq_waiters;
spin_lock_t wq_lock;
};
typedef void (*work_func_t)(struct work_item *);
struct work_item {
void *w_data;
work_func_t w_func;
struct queue_entry w_head;
};
struct worker_pool {
struct thread **wp_workers;
size_t wp_nworkers;
};
struct workqueue {
spin_lock_t wq_lock;
struct queue wq_queue; /* list of struct work_item */
};
extern kern_status_t sched_init(void);
extern void schedule(enum sched_mode mode);
extern void preempt_disable(void);
extern void preempt_enable(void);
extern void rq_init(struct runqueue *rq);
extern struct thread *rq_dequeue(struct runqueue *rq);
extern void rq_enqueue(struct runqueue *rq, struct thread *thr);
static inline void rq_lock(struct runqueue *rq, unsigned long *flags)
{
spin_lock_irqsave(&rq->rq_lock, flags);
}
static inline void rq_unlock(struct runqueue *rq, unsigned long flags)
{
spin_unlock_irqrestore(&rq->rq_lock, flags);
}
extern void rq_remove_thread(struct runqueue *rq, struct thread *thr);
extern struct runqueue *cpu_rq(unsigned int cpu);
extern struct task *task_alloc(void);
extern struct task *task_cast(struct object *obj);
extern struct task *task_create(const char *name, size_t name_len);
static inline struct task *task_ref(struct task *task)
{
return OBJECT_CAST(struct task, t_base, object_ref(&task->t_base));
}
static inline void task_unref(struct task *task)
{
object_unref(&task->t_base);
}
extern kern_status_t task_add_child(struct task *parent, struct task *child);
extern kern_status_t task_add_channel(
struct task *task,
struct channel *channel,
unsigned int id);
extern struct channel *task_get_channel(struct task *task, unsigned int id);
extern struct task *task_from_tid(tid_t id);
extern kern_status_t task_open_handle(
struct task *task,
struct object *obj,
handle_flags_t flags,
kern_handle_t *out);
extern kern_status_t task_resolve_handle(
struct task *task,
kern_handle_t handle,
struct object **out_obj,
handle_flags_t *out_flags);
extern kern_status_t task_close_handle(struct task *task, kern_handle_t handle);
extern struct thread *task_create_thread(struct task *parent);
extern struct task *kernel_task(void);
extern struct task *idle_task(void);
extern cycles_t default_quantum(void);
extern bool need_resched(void);
extern struct task *current_task(void);
extern struct thread *current_thread(void);
extern struct runqueue *select_rq_for_thread(struct thread *thr);
extern void schedule_thread_on_cpu(struct thread *thr);
extern void start_charge_period(void);
extern void end_charge_period(void);
DEFINE_OBJECT_LOCK_FUNCTION(task, t_base)
extern struct thread *thread_alloc(void);
extern struct thread *thread_cast(struct object *obj);
extern kern_status_t thread_init_kernel(struct thread *thr, virt_addr_t ip);
extern kern_status_t thread_init_user(
struct thread *thr,
virt_addr_t ip,
virt_addr_t sp,
const uintptr_t *args,
size_t nr_args);
extern int thread_priority(struct thread *thr);
extern void thread_awaken(struct thread *thr);
extern void idle(void);
extern struct thread *create_kernel_thread(void (*fn)(void));
extern struct thread *create_idle_thread(void);
extern void add_timer(struct timer *timer);
extern void remove_timer(struct timer *timer);
extern unsigned long schedule_timeout(unsigned long clock_ticks);
extern unsigned long milli_sleep(unsigned long ms);
extern void sleep_forever(void);
extern void wait_item_init(struct wait_item *item, struct thread *thr);
extern void thread_wait_begin(struct wait_item *waiter, struct waitqueue *q);
extern void thread_wait_end(struct wait_item *waiter, struct waitqueue *q);
extern void wait_on_queue(struct waitqueue *q);
extern void wakeup_queue(struct waitqueue *q);
extern void wakeup_one(struct waitqueue *q);
extern void work_item_init(work_func_t func, void *data, struct work_item *out);
extern void workqueue_init(struct workqueue *wq);
extern struct worker_pool *worker_pool_create(size_t nworkers);
extern struct worker_pool *global_worker_pool(void);
extern bool schedule_work_on(struct workqueue *wq, struct work_item *work);
extern bool schedule_work(struct work_item *work);
extern void wake_workers(struct workqueue *wq, struct worker_pool *pool);
#ifdef __cplusplus
}
#endif
#endif

183
include/kernel/syscall.h Normal file
View File

@@ -0,0 +1,183 @@
#ifndef KERNEL_SYSCALL_H_
#define KERNEL_SYSCALL_H_
#include <kernel/handle.h>
#include <kernel/vm.h>
#include <mango/status.h>
#include <mango/syscall.h>
#define validate_access(task, ptr, len, flags) \
vm_region_validate_access( \
task->t_address_space, \
(virt_addr_t)ptr, \
len, \
flags | VM_PROT_USER)
#define validate_access_r(task, ptr, len) \
validate_access(task, ptr, len, VM_PROT_READ | VM_PROT_USER)
#define validate_access_w(task, ptr, len) \
validate_access(task, ptr, len, VM_PROT_WRITE | VM_PROT_USER)
#define validate_access_rw(task, ptr, len) \
validate_access( \
task, \
ptr, \
len, \
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER)
extern kern_status_t sys_task_exit(int status);
extern kern_status_t sys_task_create(
kern_handle_t parent_handle,
const char *name,
size_t name_len,
kern_handle_t *out_task,
kern_handle_t *out_address_space);
extern kern_status_t sys_task_create_thread(
kern_handle_t task,
virt_addr_t ip,
virt_addr_t sp,
uintptr_t *args,
size_t nr_args,
kern_handle_t *out_thread);
extern kern_status_t sys_thread_start(kern_handle_t thread);
extern kern_status_t sys_vm_object_create(
const char *name,
size_t name_len,
size_t data_len,
vm_prot_t prot,
kern_handle_t *out);
extern kern_status_t sys_vm_object_read(
kern_handle_t object,
void *dst,
off_t offset,
size_t count,
size_t *nr_read);
extern kern_status_t sys_vm_object_write(
kern_handle_t object,
const void *src,
off_t offset,
size_t count,
size_t *nr_written);
extern kern_status_t sys_vm_object_copy(
kern_handle_t dst,
off_t dst_offset,
kern_handle_t src,
off_t src_offset,
size_t count,
size_t *nr_copied);
extern kern_status_t sys_vm_region_create(
kern_handle_t parent,
const char *name,
size_t name_len,
off_t offset,
size_t region_len,
vm_prot_t prot,
kern_handle_t *out,
virt_addr_t *out_base_address);
extern kern_status_t sys_vm_region_read(
kern_handle_t region,
void *dst,
off_t offset,
size_t count,
size_t *nr_read);
extern kern_status_t sys_vm_region_write(
kern_handle_t region,
const void *src,
off_t offset,
size_t count,
size_t *nr_read);
extern kern_status_t sys_vm_region_map_absolute(
kern_handle_t region,
virt_addr_t map_address,
kern_handle_t object,
off_t object_offset,
size_t length,
vm_prot_t prot,
virt_addr_t *out_base_address);
extern kern_status_t sys_vm_region_map_relative(
kern_handle_t region,
off_t region_offset,
kern_handle_t object,
off_t object_offset,
size_t length,
vm_prot_t prot,
virt_addr_t *out_base_address);
extern kern_status_t sys_vm_region_unmap_absolute(
kern_handle_t region,
virt_addr_t address,
size_t length);
extern kern_status_t sys_vm_region_unmap_relative(
kern_handle_t region,
off_t offset,
size_t length);
extern kern_status_t sys_kern_log(const char *s);
extern kern_status_t sys_kern_handle_close(kern_handle_t handle);
extern kern_status_t sys_kern_config_get(
kern_config_key_t key,
void *ptr,
size_t len);
extern kern_status_t sys_kern_config_set(
kern_config_key_t key,
const void *ptr,
size_t len);
extern kern_status_t sys_channel_create(
unsigned int id,
channel_flags_t flags,
kern_handle_t *out);
extern kern_status_t sys_port_create(kern_handle_t *out);
extern kern_status_t sys_port_connect(
kern_handle_t port,
tid_t task_id,
unsigned int channel_id);
extern kern_status_t sys_port_disconnect(kern_handle_t port);
extern kern_status_t sys_msg_send(
kern_handle_t port,
msg_flags_t flags,
const struct msg *req,
struct msg *resp);
extern kern_status_t sys_msg_recv(
kern_handle_t channel,
msg_flags_t flags,
msgid_t *out_id,
struct msg *out_msg);
extern kern_status_t sys_msg_reply(
kern_handle_t channel,
msg_flags_t flags,
msgid_t id,
const struct msg *reply);
extern kern_status_t sys_msg_read(
kern_handle_t channel,
msgid_t id,
size_t offset,
struct iovec *out,
size_t nr_out);
extern kern_status_t sys_msg_read_handles(
kern_handle_t channel,
msgid_t id,
size_t offset,
struct handle_list *out,
size_t nr_out);
extern kern_status_t sys_msg_write(
kern_handle_t channel,
msgid_t id,
size_t offset,
const struct iovec *in,
size_t nr_in);
extern kern_status_t sys_msg_write_handles(
kern_handle_t channel,
msgid_t id,
size_t offset,
const struct handle_list *in,
size_t nr_in);
extern virt_addr_t syscall_get_function(unsigned int sysid);
#endif

14
include/kernel/test.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef KERNEL_TEST_H_
#define KERNEL_TEST_H_
#ifdef __cplusplus
extern "C" {
#endif
extern int run_all_tests(void);
#ifdef __cplusplus
}
#endif
#endif

18
include/kernel/types.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef KERNEL_TYPES_H_
#define KERNEL_TYPES_H_
#include <mango/types.h>
#include <stddef.h>
#include <stdint.h>
#define CYCLES_MAX UINT64_MAX
typedef uint64_t cycles_t;
typedef uint64_t sectors_t;
struct boot_module {
phys_addr_t mod_base;
size_t mod_size;
};
#endif

70
include/kernel/util.h Normal file
View File

@@ -0,0 +1,70 @@
#ifndef KERNEL_UTIL_H_
#define KERNEL_UTIL_H_
#include <mango/types.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define CLAMP(x, lo, hi) (MIN(MAX(x, lo), hi))
extern uint64_t hash_string(const char *s);
extern void data_size_to_string(size_t value, char *out, size_t outsz);
static inline bool power_of_2(size_t x)
{
return (x > 0 && (x & (x - 1)) == 0);
}
static inline unsigned long long div64_pow2(
unsigned long long x,
unsigned long long y)
{
return x >> (__builtin_ctz(y));
}
static inline unsigned long long absdiff64(
unsigned long long x,
unsigned long long y)
{
return x < y ? y - x : x - y;
}
extern int16_t host_to_big_i16(int16_t v);
extern int16_t host_to_little_i16(int16_t v);
extern int16_t big_to_host_i16(int16_t v);
extern int16_t little_to_host_i16(int16_t v);
extern uint16_t host_to_big_u16(uint16_t v);
extern uint16_t host_to_little_u16(uint16_t v);
extern uint16_t big_to_host_u16(uint16_t v);
extern uint16_t little_to_host_u16(uint16_t v);
extern int32_t host_to_big_i32(int32_t v);
extern int32_t host_to_little_i32(int32_t v);
extern int32_t big_to_host_i32(int32_t v);
extern int32_t little_to_host_i32(int32_t v);
extern uint32_t host_to_big_u32(uint32_t v);
extern uint32_t host_to_little_u32(uint32_t v);
extern uint32_t big_to_host_u32(uint32_t v);
extern uint32_t little_to_host_u32(uint32_t v);
extern int64_t host_to_big_i64(int64_t v);
extern int64_t host_to_little_i64(int64_t v);
extern int64_t big_to_host_i64(int64_t v);
extern int64_t little_to_host_i64(int64_t v);
extern uint64_t host_to_big_u64(uint64_t v);
extern uint64_t host_to_little_u64(uint64_t v);
extern uint64_t big_to_host_u64(uint64_t v);
extern uint64_t little_to_host_u64(uint64_t v);
extern bool fill_random(void *buffer, unsigned int size);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,91 @@
#ifndef KERNEL_VM_OBJECT_H_
#define KERNEL_VM_OBJECT_H_
#include <kernel/locks.h>
#include <kernel/object.h>
#define VM_OBJECT_NAME_MAX 64
enum vm_object_flags {
/* the memory behind this vm-object wasn't allocated by us, and
* therefore shouldn't be freed by us */
VMO_IN_PLACE = 0x01u,
};
struct vm_object {
struct object vo_base;
char vo_name[VM_OBJECT_NAME_MAX];
enum vm_object_flags vo_flags;
/* queue of struct vm_region_mapping */
struct queue vo_mappings;
/* memory protection flags. mappings of this vm_object can only use
* a subset of the flags set in this mask. */
vm_prot_t vo_prot;
/* btree of vm_pages that have been allocated to this vm_object.
* vm_page->p_vmo_offset and the size of each page is the bst key. */
struct btree vo_pages;
/* total length of the vm_object in bytes. */
size_t vo_size;
};
extern kern_status_t vm_object_type_init(void);
extern struct vm_object *vm_object_cast(struct object *obj);
/* create a vm_object with the specified length in bytes and protection flags.
* the length will be automatically rounded up to the nearest vm_object page
* order size. the actual page frames themselves won't be allocated until
* they are mapped and accessed. */
extern struct vm_object *vm_object_create(
const char *name,
size_t name_len,
size_t data_len,
vm_prot_t prot);
/* create a vm_object that represents the specified range of physical memory.
* the length will be automatically rounded up to the nearest vm_object page
* order size.
* NOTE this function assumes that the physical memory has already been
* reserved, and is not in use by any other kernel component. */
extern struct vm_object *vm_object_create_in_place(
const char *name,
size_t name_len,
phys_addr_t base,
size_t data_len,
vm_prot_t prot);
extern struct vm_page *vm_object_get_page(
const struct vm_object *vo,
off_t offset);
extern struct vm_page *vm_object_alloc_page(
struct vm_object *vo,
off_t offset,
enum vm_page_order size);
extern kern_status_t vm_object_read(
struct vm_object *vo,
void *dst,
off_t offset,
size_t count,
size_t *nr_read);
extern kern_status_t vm_object_write(
struct vm_object *vo,
const void *dst,
off_t offset,
size_t count,
size_t *nr_written);
extern kern_status_t vm_object_copy(
struct vm_object *dst,
off_t dst_offset,
struct vm_object *src,
off_t src_offset,
size_t count,
size_t *nr_copied);
DEFINE_OBJECT_LOCK_FUNCTION(vm_object, vo_base)
#endif

146
include/kernel/vm-region.h Normal file
View File

@@ -0,0 +1,146 @@
#ifndef KERNEL_VM_REGION_H_
#define KERNEL_VM_REGION_H_
#include <kernel/object.h>
#include <kernel/pmap.h>
#include <kernel/vm.h>
#define VM_REGION_NAME_MAX 64
#define VM_REGION_COPY_ALL ((size_t)-1)
struct vm_region;
struct vm_object;
enum vm_region_entry_type {
VM_REGION_ENTRY_NONE = 0,
VM_REGION_ENTRY_REGION,
VM_REGION_ENTRY_MAPPING,
};
struct vm_region_entry {
struct btree_node e_node;
struct vm_region_entry *e_parent;
enum vm_region_entry_type e_type;
/* offset in bytes of this entry within its immediate parent. */
off_t e_offset;
/* size of the entry in bytes */
size_t e_size;
};
struct vm_region_mapping {
struct vm_region_entry m_entry;
struct vm_object *m_object;
/* used to link to vm_object->vo_mappings */
struct queue_entry m_object_entry;
vm_prot_t m_prot;
/* offset in bytes to the start of the object data that was mapped */
off_t m_object_offset;
};
struct vm_region {
struct object vr_base;
struct vm_region_entry vr_entry;
char vr_name[VM_REGION_NAME_MAX];
/* btree of struct vm_region_entry.
* sibling entries cannot overlap each other, and child entries must
* be entirely contained within their immediate parent entry. */
struct btree vr_entries;
/* memory protection restriction mask.
* any mapping in this region, or any of its children, cannot use
* protection flags that are not set in this mask.
* for example, if VM_PROT_EXEC is /not/ set here, no mapping
* can be created in this region or any child region with VM_PROT_EXEC
* set. */
vm_prot_t vr_prot;
/* the physical address space in which mappings in this region (and
* its children) are created */
pmap_t vr_pmap;
};
extern kern_status_t vm_region_type_init(void);
extern struct vm_region *vm_region_cast(struct object *obj);
/* create a new vm-region, optionally within a parent region.
* `offset` is the byte offset within the parent region where the new region
* should start.
* if no parent is specified, `offset` is the absolute virtual address of the
* start of the region.
* in both cases, `len` is the length of the new region in bytes. */
extern kern_status_t vm_region_create(
struct vm_region *parent,
const char *name,
size_t name_len,
off_t offset,
size_t region_len,
vm_prot_t prot,
struct vm_region **out);
/* map a vm-object into a vm-region.
* [region_offset,length] must fall within exactly one region, and cannot span
* multiple sibling regions.
* if [region_offset,length] falls within a child region, the map operation
* will be transparently redirected to the relevant region.
* `prot` must be allowed both by the region into which the mapping is being
* created AND the vm-object being mapped. */
extern kern_status_t vm_region_map_object(
struct vm_region *region,
off_t region_offset,
struct vm_object *object,
off_t object_offset,
size_t length,
vm_prot_t prot,
virt_addr_t *out);
extern kern_status_t vm_region_unmap(
struct vm_region *region,
off_t region_offset,
size_t length);
extern bool vm_region_validate_access(
struct vm_region *region,
off_t offset,
size_t len,
vm_prot_t prot);
/* find the mapping corresponding to the given virtual address, and page-in the
* necessary vm_page to allow the memory access to succeed. if the relevant
* vm-object page hasn't been allocated yet, it will be allocated here. */
extern kern_status_t vm_region_demand_map(
struct vm_region *region,
virt_addr_t addr,
enum pmap_fault_flags flags);
/* get the absolute base virtual address of a region within its
* parent/ancestors. */
extern virt_addr_t vm_region_get_base_address(const struct vm_region *region);
extern void vm_region_dump(struct vm_region *region);
extern kern_status_t vm_region_memmove(
struct vm_region *dest_region,
virt_addr_t dest_ptr,
struct vm_region *src_region,
virt_addr_t src_ptr,
size_t count,
size_t *nr_moved);
extern kern_status_t vm_region_memmove_v(
struct vm_region *dest_region,
size_t dest_offset,
struct iovec *dest,
size_t nr_dest,
struct vm_region *src_region,
size_t src_offset,
const struct iovec *src,
size_t nr_src,
size_t bytes_to_move);
DEFINE_OBJECT_LOCK_FUNCTION(vm_region, vr_base)
#endif

358
include/kernel/vm.h Normal file
View File

@@ -0,0 +1,358 @@
#ifndef KERNEL_VM_H_
#define KERNEL_VM_H_
#include <kernel/bitmap.h>
#include <kernel/btree.h>
#include <kernel/locks.h>
#include <kernel/machine/vm.h>
#include <kernel/queue.h>
#include <kernel/types.h>
#include <mango/status.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
struct bcache;
/* maximum number of NUMA nodes */
#define VM_MAX_NODES 64
/* maximum number of memory zones per node */
#define VM_MAX_ZONES (VM_ZONE_MAX + 1)
/* maximum number of supported page orders */
#define VM_MAX_PAGE_ORDERS (VM_PAGE_MAX_ORDER + 1)
/* maximum number of sparse memory sectors */
#define VM_MAX_SECTORS 8192
/* maximum number of disk sectors that can be stored in a single
page. AKA the number of bits in the sector bitmap.
used by the block cache */
#define VM_MAX_SECTORS_PER_PAGE 32
#define VM_CHECK_ALIGN(p, mask) ((((p) & (mask)) == (p)) ? 1 : 0)
#define VM_CACHE_INITIALISED(c) ((c)->c_obj_count != 0)
#define VM_PAGE_IS_FREE(pg) \
(((pg)->p_flags & (VM_PAGE_RESERVED | VM_PAGE_ALLOC)) == 0)
#define vm_page_foreach(pg, i) \
for (struct vm_page *i = (pg); i; i = vm_page_get_next_tail(i))
typedef phys_addr_t vm_alignment_t;
typedef unsigned int vm_node_id_t;
enum vm_model {
VM_MODEL_FLAT = 1,
VM_MODEL_SPARSE,
};
enum vm_flags {
VM_NORMAL = 0x00u,
VM_GET_DMA = 0x01u,
};
enum vm_zone_id {
/* NOTE that these are used as indices into the node_zones array in
vm/zone.c they need to be continuous, and must start at 0!
not all of these zones are implemented for every architecture. */
VM_ZONE_DMA = 0u,
VM_ZONE_NORMAL = 1u,
VM_ZONE_HIGHMEM = 2u,
};
enum vm_page_order {
VM_PAGE_4K = 0u,
VM_PAGE_8K,
VM_PAGE_16K,
VM_PAGE_32K,
VM_PAGE_64K,
VM_PAGE_128K,
VM_PAGE_256K,
VM_PAGE_512K,
VM_PAGE_1M,
VM_PAGE_2M,
VM_PAGE_4M,
VM_PAGE_8M,
VM_PAGE_16M,
VM_PAGE_32M,
VM_PAGE_64M,
VM_PAGE_128M,
/* struct vm_page only has 4 bits to store the page order with.
the maximum order that can be stored in 4 bits is 15 (VM_PAGE_128M)
to use any of the page orders listed here, this field
will have to be expanded. */
VM_PAGE_256M,
VM_PAGE_512M,
VM_PAGE_1G,
VM_PAGE_2G,
VM_PAGE_4G,
VM_PAGE_8G,
VM_PAGE_16G,
VM_PAGE_32G,
VM_PAGE_64G,
};
enum vm_page_flags {
/* page is reserved (probably by a call to memblock_reserve()) and
cannot be returned by any allocation function */
VM_PAGE_RESERVED = 0x01u,
/* page has been allocated by a zone's buddy allocator, and is in-use */
VM_PAGE_ALLOC = 0x02u,
/* page is the first page of a huge-page */
VM_PAGE_HEAD = 0x04u,
/* page is part of a huge-page */
VM_PAGE_HUGE = 0x08u,
/* page is holding cached data from secondary storage, and can be freed
* if necessary (and not dirty). */
VM_PAGE_CACHE = 0x10u,
};
enum vm_memory_region_status {
VM_REGION_FREE = 0x01u,
VM_REGION_RESERVED = 0x02u,
};
enum vm_cache_flags {
VM_CACHE_OFFSLAB = 0x01u,
VM_CACHE_DMA = 0x02u
};
struct vm_zone_descriptor {
enum vm_zone_id zd_id;
vm_node_id_t zd_node;
const char zd_name[32];
phys_addr_t zd_base;
phys_addr_t zd_limit;
};
struct vm_zone {
struct vm_zone_descriptor z_info;
spin_lock_t z_lock;
struct queue z_free_pages[VM_MAX_PAGE_ORDERS];
unsigned long z_size;
};
struct vm_pg_data {
struct vm_zone pg_zones[VM_MAX_ZONES];
};
struct vm_cache {
const char *c_name;
enum vm_cache_flags c_flags;
struct queue_entry c_list;
struct queue c_slabs_full;
struct queue c_slabs_partial;
struct queue c_slabs_empty;
spin_lock_t c_lock;
/* number of objects that can be stored in a single slab */
unsigned int c_obj_count;
/* the size of object kept in the cache */
unsigned int c_obj_size;
/* combined size of struct vm_slab and the freelist */
unsigned int c_hdr_size;
/* power of 2 alignment for objects returned from the cache */
unsigned int c_align;
/* offset from one object to the next in a slab.
this may be different from c_obj_size depending
on the alignment settings for this cache. */
unsigned int c_stride;
/* size of page used for slabs */
unsigned int c_page_order;
};
struct vm_slab {
struct vm_cache *s_cache;
/* queue entry for struct vm_cache.c_slabs_* */
struct queue_entry s_list;
/* pointer to the first object slot. */
void *s_objects;
/* the number of objects allocated on the slab. */
unsigned int s_obj_allocated;
/* the index of the next free object.
if s_free is equal to FREELIST_END (defined in vm/cache.c)
there are no free slots left in the slab. */
unsigned int s_free;
/* list of free object slots.
when allocating:
- s_free should be set to the value of s_freelist[s_free]
when freeing:
- s_free should be set to the index of the object being freed.
- s_freelist[s_free] should be set to the previous value of s_free.
this is commented as it as flexible arrays are not supported in c++.
*/
// unsigned int s_freelist[];
};
struct vm_page {
/* order of the page block that this page belongs too */
uint32_t p_order : 4;
/* the id of the NUMA node that this page belongs to */
uint32_t p_node : 6;
/* the id of the memory zone that this page belongs to */
uint32_t p_zone : 3;
/* vm_page_flags_t bitfields. */
uint32_t p_sector : 11;
/* some unused bits */
uint32_t p_reserved : 8;
uint32_t p_flags;
/* owner-specific pointer */
union {
struct vm_slab *p_slab;
struct bcache *p_bcache;
void *p_priv0;
};
/* multi-purpose list/tree entry.
the owner of the page can decide what to do with this.
some examples:
- the buddy allocator uses this to maintain its per-zone free-page
lists.
- vm_object uses it to maintain a btree of allocated pages keyed
by offset/size.
- the block cache uses this to maintain a tree of pages keyed by
block number.
*/
union {
struct queue_entry p_list;
struct btree_node p_bnode;
/* btree_node contains three pointers, so provide three
pointer-sized integers for use if p_bnode isn't needed. */
uintptr_t priv1[3];
};
union {
/* used by bcache when sector size is < page size. bitmap of
* present/missing sectors */
DECLARE_BITMAP(p_blockbits, VM_MAX_SECTORS_PER_PAGE);
uint32_t p_priv2;
};
union {
/* sector address, used by bcache */
sectors_t p_blockid;
/* offset of this page within the vm_object it is a part of */
off_t p_vmo_offset;
uint32_t p_priv3[2];
};
} __aligned(2 * sizeof(unsigned long));
/* represents a sector of memory, containing its own array of vm_pages.
this struct is used under the sparse memory model, instead of the
global vm_page array */
struct vm_sector {
/* sector size. this must be a power of 2.
all sectors in the system have the same size. */
enum vm_page_order s_size;
/* PFN of the first page contained in s_pages.
to find the PFN of any page contained within s_pages,
simply add its offset within the array to s_first_pfn */
size_t s_first_pfn;
/* array of pages contained in this sector */
struct vm_page *s_pages;
};
extern kern_status_t vm_bootstrap(
const struct vm_zone_descriptor *zones,
size_t nr_zones);
extern enum vm_model vm_memory_model(void);
extern void vm_set_memory_model(enum vm_model model);
extern struct vm_pg_data *vm_pg_data_get(vm_node_id_t node);
extern phys_addr_t vm_virt_to_phys(const void *p);
extern void *vm_phys_to_virt(phys_addr_t p);
extern size_t vm_page_order_to_bytes(enum vm_page_order order);
extern size_t vm_page_order_to_pages(enum vm_page_order order);
extern vm_alignment_t vm_page_order_to_alignment(enum vm_page_order order);
extern void vm_page_init_array(void);
extern struct vm_page *vm_page_get(phys_addr_t addr);
extern phys_addr_t vm_page_get_paddr(struct vm_page *pg);
extern struct vm_zone *vm_page_get_zone(struct vm_page *pg);
extern void *vm_page_get_vaddr(struct vm_page *pg);
extern size_t vm_page_get_pfn(struct vm_page *pg);
static inline size_t vm_page_get_size_bytes(const struct vm_page *pg)
{
return vm_page_order_to_bytes(pg->p_order);
}
extern struct vm_page *vm_page_alloc(
enum vm_page_order order,
enum vm_flags flags);
extern void vm_page_free(struct vm_page *pg);
extern int vm_page_split(
struct vm_page *pg,
struct vm_page **a,
struct vm_page **b);
extern struct vm_page *vm_page_merge(struct vm_page *a, struct vm_page *b);
extern struct vm_page *vm_page_get_buddy(struct vm_page *pg);
extern struct vm_page *vm_page_get_next_tail(struct vm_page *pg);
extern size_t vm_bytes_to_pages(size_t bytes);
extern void vm_zone_init(
struct vm_zone *z,
const struct vm_zone_descriptor *zone_info);
extern struct vm_page *vm_zone_alloc_page(
struct vm_zone *z,
enum vm_page_order order,
enum vm_flags flags);
extern void vm_zone_free_page(struct vm_zone *z, struct vm_page *pg);
extern struct vm_cache *vm_cache_create(
const char *name,
size_t objsz,
enum vm_cache_flags flags);
extern void vm_cache_init(struct vm_cache *cache);
extern void vm_cache_destroy(struct vm_cache *cache);
extern void *vm_cache_alloc(struct vm_cache *cache, enum vm_flags flags);
extern void vm_cache_free(struct vm_cache *cache, void *p);
extern void kmalloc_init(void);
extern void *kmalloc(size_t count, enum vm_flags flags);
extern void *kzalloc(size_t count, enum vm_flags flags);
extern void kfree(void *p);
/* Flat memory model functions */
extern void vm_flat_init(void);
extern struct vm_page *vm_page_get_flat(phys_addr_t addr);
extern size_t vm_page_get_pfn_flat(struct vm_page *pg);
/* Sparse memory model functions */
extern void vm_sparse_init(void);
extern struct vm_page *vm_page_get_sparse(phys_addr_t addr);
extern size_t vm_page_get_pfn_sparse(struct vm_page *pg);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
inline void *operator new(size_t count, void *p)
{
return p;
}
#define kmalloc_object(objtype, flags, ...) \
__extension__({ \
void *p = kmalloc(sizeof(objtype), flags); \
if (p) { \
new (p) objtype(__VA_ARGS__); \
} \
(objtype *)p; \
})
#endif
#endif