#Keyronex
1 messages ยท Page 4 of 1
for a regular file the semantics of 9p2000.L and FUSE alike is that having it open = it persists on the server side
but directories, they're weird?
yeah, those i need to handle more carefully
the vnode ops i use are a close match to those described in the paper on SunOS' VFS, and those were specifically designed for NFS
so there shouldn't be any fundamental incompatibilities
Is this the paper by klienman or smth like that?
that's the one
hmm where the hell is the iso??
Pattern 'keyronex-weekly-2023-05-10.iso' does not match any files. wow thanks actions
ugh i messed up
of course i did
we should do a tarball of system-root as well for convenience
diff --git a/.github/workflows/weekly.yml b/.github/workflows/weekly.yml
index d622670..3bba39b 100644
--- a/.github/workflows/weekly.yml
+++ b/.github/workflows/weekly.yml
@@ -41,13 +41,13 @@ jobs:
- name: Build everything
run: make all
+ - name: Set TAG_DATE environment variable
+ run: echo "TAG_DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
+
- name: Build and rename ISO
run: |
make iso
- mv build/barebones.iso keyronex-weekly-$(date +'%Y%m%d').iso
-
- - name: Set TAG_DATE environment variable
- run: echo "TAG_DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
+ mv build/barebones.iso keyronex-weekly-$TAG_DATE.iso
- name: Tag and push new release tag
run: |
oh do the iso's not bundle the initrd?
thats right
ah, that explains what went awry
we just built everything for nothing then ๐
they just mount the system-root with 9p or fuse atm
ahh that makes sense
i wonder how small a minimal bash, coreutils, mlibc combination would be though
could be worth doing such an iso that extracts a tar into a tmpfs
it would still take 30-40 ish minutes
most of it is taken up by gcc
compiling the cross compiler
it takes around 30 minutes sadly
in that case we may as well build everything and dispense the complete system root as a tarball
alright, ill prepare a pr that hopefully isnt broken this time
sorry for the oversight
no worries, we now have an actual weekly build going on, which is a big improvement over the previous state of affairs where only i could build this thing
the sysroot seems to be quite big on my end
i wonder how big the archive ends up being
148M gzip compressed
xz was taking forever
it's inconsistently stripped because i like a lot of things to be unstripped for debuggability, but that drastically increaes the size
148m seems reasonable
and i can see uncompressed it's 674m
oh its running inside keyronex, that's neat
i had a sudden desire to run df -h and stat -f so threw it together over the weekend
do you prefer tag names with hyphens or without?
weekly-yyyy-mm-dd or weekly-yyyymmdd
funny you ask that
i was experimenting with how i label my files at placement
and i struggled with yymmdd or yyyymmdd but yy-mm-dd was much easier for me to parse
try zstd maybe? 
not a bad idea
what is the flag i have to pass to tar for it to use zstd?
--zstd, ugh
damn zstd is 3 times as fast and produces an even smaller archive
ill take that!
i'm happy with that
and do you want to use yy-mm-dd for the date?
or do you want to stick with full 4 digit year?
might as well use the 4-digit year, i think that's more standard
alright, yyyy-mm-dd it is
it will also clear up confusion for people trying to download keyronex in 2123
keyronex-yyyy-mm-dd.iso
keyronex-sysroot-yyyy-mm-dd.tar.zst
lets hope everything goes as planned
hmm let me modify the workflow to place an empty file into the system root and see if everything works as planned
well except for that little detail it all seems to be working
alright testing it for the last time, if it works ill cook up a pr
aargh
use zstd
repost
best compression algo imo
I didnโt read the rest of the convo lmaooo
lol
before running another "weekly" run make sure to get rid of the existing tag and release
im not entirely sure how to do that though
apparently git push --delete origin weekly-2023-05-10 should work just fine, but i havent tested it
actually let me lol
oh that's neat
czapek@~/sources/Keyronex-fork$ git push --delete origin weekly-2023-05-10
To github.com:czapek1337/Keyronex.git
- [deleted] weekly-2023-05-10
yeah that totally works
even the C++ stdlib has this meme https://en.cppreference.com/w/cpp/thread/lock
i had to do this for some reason
'delete tag' was greyed out again
github loves greying things out, not explaining any further, and leaving
anyway, merged and started
Great minds let themselves be inspiered by other Great minds.
(note: the first G is capitalised because it is at the start of a sentence, while the second G is capitalised to emphasize the word)
congrats
at last keyronex is runnable by others
tricky business, i was just thinking of this again
what do i even do when a namecache entry times out - look it up again?
i.e. get its parent, lookup(thisnc->name), check if we get the same result?
i am just designing the new namecache now and there are quite a few interesting design questions
for example, whether it should be legal to transfer a namecache from one parent to another (to handle the case of renaming, for example), or whether the old namecache should be orphaned; i think the latter is better in terms of locking constraints
not quite sure I understand you there...
moving vnodes is absolutely necessary for rename(2) (otherwise, how'd you deal with stale references to the namecache?), although it sucks from a locking point of view.
at the same time, the vnode that is being replaced (moved into) should stay
I've tested that by chdir()-ing into a directory on Linux, then removing it and moving a new directory into its place- getcwd() still returns the same path
this suggests that the vnode being moved into (aka replaced) is orphaned
although I think we mean different things when we say orphaned
for me 'orphaned' means removed from the parents' list of children, and losing its parent pointer
to avoid moving i had been thinking of invalidating the old namecache
orphaning it even
then we have the same definition of "orphaned", except that my vnodes would keep their parent pointers
how do you handle moving directories that have a lot of stuff in them?
you'd have to invalidate a hell lot of namecaches for that
it would only be the one namecache invalidated
but all the children?
they retain a reference to it
so it would persist
but i suspect there is another problem with doing this which i haven't thought of yet
I guess a potential solution to the child problem would be to have orphaned namecaches have a pointer to the "real" namecache, and any time a child needs to get the parent (for lookups on '..', for example), if the parent is an orphaned namecache, it replaces its parent pointer
jesus fkn christ, I'm gonna get banned from discord because of the words I use
moving children?
i was thinking of something along this lines
moved namecaches acquire a "replacement" pointer to a new namecache
how would the new namecache handle lookups on children?
i suppose it would have to check what it replaced, but then we run into a reference cycle
I think for a proper rename(2), stuff is going to get moved regardless of what you do
I made the mistake in an earlier iteration of osdev (before I even had a clue what good design was) of designing the vnode layer to be immutable, no vnodes could be moved whatsoever, in any way ever
yes, i'm beginning to come round to that myself
i suppose what i worried about is that mutability of namecaches would be a cause for confusion
well, everything is mutable unless it is locked
not really
but with the VFS, that's how you should think
(at least I think so)
what i hoped to do was to avoid locking a namecache on every e.g. read/write/ioctl
otherwise namecache->inode can't be safely gotten
inode-vnode separation :^)
even if i atomically load namecache->inode i am not safe yet because i need to reference it
without the reference it can and will go away
well I propose a solution to you:
on unlink(), you don't actually remove the reference to the inode from the namecache, you instead orphan the namecache
I myself have been thinking of that
that way, namecache->inode can only change if it is NULL
meaning that whenever you know that namecache->inode is non-null, it can be considered const
oh, that seems like it might actually work rather well
i don't think there can ever be a transition from one inode to another
but aye, with unlink i think orphaning is the appropriate choice
after all i envisage the namecache handle becoming the thing that a kernel file description refers to, so it's important to preserve the inode for continued operations on it
it'd also match this behaviour
why not let the kernel file struct point to both the namecache handle and the inode?
it wouldn't hurt
well, having established that a namecache's inode pointer can only ever transition from NULL to a valid inode, i.e. only once in its lifetime, this would no longer be harmful, as a file_t can only refer to something backed by a valid inode
except for in the future when you'll maybe want to have a file descriptor that refers to a path (O_PATH) or a file descriptor that opens an inode but with a fake path in /proc/abc/fd/x
bollocks, i just remembered about procfs
now i need to think about how pid-specific namecaching is done
how would pid-specific namecaching differ from any other namecaching?
surely the filesystem driver would be the only thing that differs
readlink("/proc/<pid>/fd/<x>") would be implemented by taking a look at file_t->namecache and prepending to a string
it's /proc/self that i'm concerned about
/proc/self can just be a symlink with readlink() being
int proc_self_readlink(blablabla, const char **out)
{
*out = asprintf("%d", current_pid());
return (*out) ? 0 : ENOMEM;
}
assuming that readlink() isn't cached
which, if it is, could be changed with a simple vnode/namecache flag
by god, that might actually do the trick
i'm not sure why i had it in my head that /proc/self has to be/appear to be a true directory
lr-xr-xr-x 1 root wheel 3 May 11 20:22 self -> curproc
and for curproc:
lr-xr-xr-x 1 root wheel 2 May 11 20:22 curproc -> 40
genius, a simple solution
curproc -> 40
that's a bit unrealistic
for me, ls -l /proc/self usually returns something above 3000
I guess PID allocation differs between netbsd and linux
in this case i quite literally just went over and booted my desktop
that makes sense, I guess
i am typing from my laptop's (boo, hiss) windows partition
my linux machine might actually be quite bloated for a linux machine
the result will be influenced by netbsd kernel threads not enjoying a PID number
but to be fair, I installed linux onto this computer like three years ago when I had literally zero experience with any advanced programming (I don't consider OpenGL "Hello Triangle" or a "Hello World" multiboot kernel advanced)
incidentally i think a readlink cache is worth implementing but will certainly need to be possible to switch it off for the procfs
PID numbers are an interesting thing
per POSIX, do TIDs have to be unique in a process or across the entire system?
afaik, on Linux, TIDs are unique across the entire system, so I'm a bit unsure
lots of activity in here
they are non-unique on netbsd, e.g.:
root 1 1 0.0 0.1 19848 1620 ? Is 8:20PM 0:00.10 init
root 166 1 0.0 0.1 32516 2284 ? Ss 8:21PM 0:00.06 /usr/sbin/syslogd -s ```
username, pid, lwp id (tid equivalent)
A correct VFS is a very complex thing to design
just wing it /j
everything seems to get horribly complex when you want to give even the slightest consideration to edge cases
rename(2) - the bane of any VFS designer
and with even only fairly-granular locking it gets worse
actually, add mount -B to the list of things that are the bane of any VFS designer
dragonfly bsd cleverly replicates parts of its namecache on a per-cpu basis and they have sophisticated cache-coherence mechanisms matt dillon developed with a view to clusters
thanks for linking that, it will be useful
currently experimenting with whether i can keep a struct namecache under 128 bytes
i've barely squeezed it into 120 bytes
@paper fox i think you might like this
vnodes might be kept, 'weakly referenced', in an inode number to vnode cache of the FS (or similar)
[18:27]Yehuda: so vnodes' destructor (they are managed by the object manager), it calls the 'inactive' vnode method, in a typical FS that locks the FS' node cache, it then checks if the vnode is still refcnt = 0, if so there's no way for a reference to be retained anymore and it makes away with the vnode. but if refcnt > 0, then it abandons the destruction and returns
[18:30]Yehuda: but this isn't enough. consider this: vnode destructor is entered (as --refcnt == 0). it locks the node cache mutex and discovers that the vnode refcnt is still 0. it removes it from the node cache and frees it. but before it got around to acquiring the node cache mutex, another thread already has gotten the vnode from the node cache and then released it again, bringing its refcnt to 0. the other thread now also wants to enter the destructor. but the first destructor has now acquired the node cache mutex and it's DELETED the vnode!
[18:32]Yehuda: i think i can solve it by, instead of the destructor being called after the object manager reduced vnode's refcnt to 0, instead calling the destructor when the refcnt is found to be 1 by the object manager's release-an-object function
[18:33]Yehuda: the destructor must lock the node cache, check if the vnode refcnt is still 1, if so it does the deletion, else it must now do the deferred reference count reduction and return
Currently I just do the trivial thing, lock the vnode->parent so that no new refs to the vnode can be acquired, decrement the refcount, if it isn't zero, return, remove and destroy the vnode.
Suboptimal, puts some constraints on vput(), but it works
In the future I might have some generation counter or use some "deferred vnode freeing" scheme, where I offload the work of vput() to a separate kernel thread.
i have the trouble of a centralised object refcounting framework
so it decrements refcount before doing object-specific logic
that will have to change now
rather this:
void myobj_put(struct myobj *obj)
{
if(!drop(&obj->refcnt))
return;
/* destroy the object */
}
same idea
obviously, certain things need to be more complicated than that
i think my c style guide forbids ! for non-booleans
atm vput() looks like this:
void vput(struct vnode *vnode)
{
begin:
spin_acquire(&vnode->rename_lock);
struct vnode *parent = vnode->parent;
if(parent != vnode)
spin_acquire(&parent->lock);
if(!drop(&vnode->refcnt)) {
if(parent != vnode)
spin_release(&parent->lock);
spin_release(&vnode->rename_lock);
return;
}
if(vnode->inode)
iput(vnode->inode);
if(parent != vnode) {
__avl_delete(&vnode->entry, &parent->children, NULL);
spin_release(&parent->lock);
}
if(vnode->name != vnode->__inline_name)
kfree((void *) vnode->name);
free_vnode(vnode);
if(parent != vnode) {
vnode = parent;
goto begin;
}
}
horrendous
needs a rework
how big are your vnodes out of interest?
I have no idea, but I'll count it now
this is what my namecaches look like (128 bytes):
struct namecache {
kmutex_t mutex; /*!< longer-term lock */
uint32_t refcnt; /*!< count of retaining references */
uint8_t name_len; /*!< length of name, max 255 */
uint32_t unused : 24; /*!< can become flags in the future */
TAILQ_ENTRY(namecache) lru_entry; /*!< linkage in LRU list*/
RB_ENTRY(namecache) rb_entry; /*!< linkage in parent->entries */
RB_HEAD(namecache_rb, namecache) entries; /*!< (m) names in directory */
struct namecache *parent; /*!< (l to read) parent directory namecache */
struct vnode *vp; /*!< underlying vnode or NULL if a negative */
char *name; /*!< filename */
uint64_t key; /*!< rb key: len << 32 | hash(name) */
uint64_t unused2; /*!< could use this space to store an inline name? */
};
apparently my struct vnode is 120 bytes
struct vnode {
/*
* This is the link followed for 'chdir("..")'-type lookups. For root
* vnodes, this is a pointer to self.
*/
struct vnode *parent;
struct avlnode entry;
/*
* For short vnode names (<20), we store the name embedded in the vnode.
* This also aligns the struct fields a bit nicer when on a 64-bit
* system.
*/
char __inline_name[VNODE_NAME_INLINE_LEN + 1];
const char *name;
/*
* Protects the above fields against renames.
*/
spinlock_t rename_lock;
/*
* A lock for the 'inode' field, mount list, and child tree.
*/
spinlock_t lock;
/*
* List of mounts that shadow this vnode.
* Protected by the above lock.
*/
struct list mount_list;
/*
* Contained inode, or NULL.
*/
struct inode *inode;
struct avltree children;
/*
* Don't use this direcly, use vget() and vput() instead.
*/
refcnt_t refcnt;
};
I could (and probably should) increase VNODE_NAME_INLINE_LEN from 19 to 27
mine are made large by needing a kmutex_t which is 32 bytes as i can't use a spinlock if namecache entries are to be locked during operations in the filesystem (IPL is elevated during spinlock holding => yielding operations forbidden => a non-spin snychronisation object can never be inferior in lock ordering to a spin lock => filesystem ops are toast because they want to do yielding operations and work with non-spin synch objects)
do you have any recursive mutexes?
no
i have left the door open to support them but have dodged the need so far
but that's probably why my mutex has another 8 bytes (owner pointer)
For recursive mutexes, I'd have to separate the mutex implementation from the semaphore implementation (which wouldn't be too hard actually)
I want to have a singly-linked list in the mutex/semaphore struct, but I also want to be able to have waiters on timeouts or otherwise interruptible
actually
a singly-linked list would be very possible, I just realised.
nvm, it wouldn't, because I still need a tail pointer
so it'd still take up two machine-words

that's where 16 of my bytes go
all syncronisation objects can be waited on with timeouts and can be interrupted by ASTs (used for the generation of signals among other things)
there is a bottleneck which will become significant when there are around 64 cores, which is that all synchronisation objects are subject to a big spinlock. i'll relieve it in the future with something like an in-kernel futex
I guess one possible recursive mutex implementation that would work but not very well is to just have a pointer in a per-task variable ->owns_mutex, that points to a currently-owned recursive mutex, and then a per-task counter, thereby removing the need for any changes to the mutex implementation.
so for mutex_acquire_recursive():
void mutex_acquire_recursive(struct mutex *mtx)
{
struct task *me = current_task();
if(me->owns_mutex == mtx) {
me->recursive_mutex_cnt++;
return;
}
if(me->owns_mutex)
panic("This design makes it impossible to own more than one recursive mutex at once. :^(");
mutex_acquire(mtx); /* normal acquire */
me->owns_mutex = mtx;
me->recursive_mutex_cnt = 1;
}
and mutex_release_recursive() wouldn't even need a pointer to the mutex as an argument, but it'd look like this:
void mutex_release_recursive(void)
{
struct task *me = current_task();
if(!me->owns_mutex)
panic("Trying to release a recursive mutex, but not holding any!");
if(--me->recursive_mutex_cnt)
return;
mutex_release(me->owns_mutex); /* normal release */
me->owns_mutex = NULL;
}
This way it'd be possible to use the current mutex/semaphore impl as a recursive mutex/semaphore without any modifications to the data structures, but it'd not be possible to hold more than one at once.
what i had been thinking of for recursive mutexes was: i already have a counter in the common synch object header, that's used for semaphores. for mutexes, if you try to acquire it and owner == curthread(), then adjust the counter. when you release it, test if owner == curthread(), adjust counter, if counter goes back to the acquirable state, release and wake next waiter
i feel like taking a detour before i continue with namecache work
getting tempted by the prospect of enhancing & exposing tcp/udp sockets to userland
yes, thats exactly what I'll do
this looks like a promising start
too bad receiving isn't yet possible, but that can be dealt with
using slurp? @hexed solstice
bridge
i remember slirp as being a hassle, can't remember exactly what was a hassle about it thugh
cool
nice
less effort than i expected (since the fundamentals were done, just not exposed to userspace
oh wow nice
interesting, my virtio-nic driver seems to permit to send 63 requests and then it can't do any more
so that's what the problem seems to be. i never thought the virtio-nic driver would turn out to be faulty
so i've been searching everywhere else for clues. i rewrote the virtio-nic driver some time ago to be purely asynchronous. but, lo and behold, it seems i never wrote the new code to deal with used elements on the TX virtqueue
wow congrats ๐ฅณ
cheers, though as usual there is much work to do to turn the rough work done to get a nice screenshot into srong, stable features
nice congrats!
can't wait for me keyronex inception
very nice
Nice work
cheers
bloody hell, my virtio-net driver is trying to DDOS people
it's repeatedly re-enqueuing a packet for transmission. transmitted it some 100,000 times in a second
dreadful oversight i just discovered in keyronex: for all this time, vmem has not been actually making segments free under certain conditions (when it coudn't coalesce the segment with immediately prior or further segments)
it's time to port a dhcp client
or write one. (i have no rust so andy's is not an option)
if you write one make it portable in C
i've found udhcp which seems alright
just working on its prerequisites
it needs linux's PF_PACKET sockets
udhcp is now ported
i can recommend it to anyone who needs a DHCP client but, having no rust, can't use andy's one
it does need PF_PACKET sockets, which are linux's badly named link-level sockets (and therefore very easy to implement, just duplicate incoming packets before delivery to the networking stack and deliver them to the PF_PACKET sockets if the specified protocol of that socket matches the ethertype of that packet; and in turn, send data sent on the socket directly to the ethernet driver)
identified the ppoll issue i had previously been having: poll should ignore (not set POLLNVAL) negative FDs
why SHOULD there be negative fds in the first place?!
fixed the issues preventing me using esc to access links' menu: a combination of the earlier poll() problem and a particularly stupid one i just spotted in the tty driver (reading zeroes into the supplied buffer of read() up to the max size of the buffer)
convenience i suppose, it's used in the select() to poll() mapping
and then poll() itself i do as nothing but a shim over epoll(), so i hadn't even considered the differing semantics
very cool!
cheers, and many thanks for the links recipe that i stole
some day i will be brave enough to try to build it with graphics enabled
I stole it from Dennis

let's see if anyone steals it from me for a third time lucky
๐
the time has come to implement ifconfig and route
(udhcp doesn't actually set anything, it launches an external script to do it for you)
nice, i actually hadn't tried links in flanterm before so i am happy to see it seems to work fine
it's 'just worked' so far with everything i've thrown at it
i might try something really outrageous like TVision and see how it reacts to that
in the meantime i think it's time to add a proper kernel-userspace communications method so that i can do ifconfig and route without ioctl hell
i could do a routing socket, NetLink, or some kind of distributed objects styled thing with objc
wouldn't the 3rd approach require you to modify udhcp source?
udhcp invokes a script to set the actual parameters (passing them as environment variables) so it's unaffected by it
can you link me the netlink script that's presumably used on linux
the default script just uses ifconfig and route
ahh okay
the latter would be interesting to see
are there images for keyronex?
weekly builds are built weekly: https://github.com/Keyronex/Keyronex/releases/tag/weekly-2023-05-15
thank you!
there is not much exciting you can do with it and it needs to be launched with a particular command line to qemu (to provide a 9p fs)
one of these days i will arrange the weekly build to supply that script
@hexed solstice does your weekly build ever make the ci run out of the max hours?
and how much time does it take to build it
it takes just over an hour at the moment, but it'll probably shoot up higher when i add a GCC for keyronex host to the build
and if i added llvm probably even longer. i noticed that github limits one job to 6 hours but a workflow can last 35 days (!) so it might be possible to optimise things - i know the xbstrap command says it has functionality like "download-archive" and "download-tool-archive", i've not seen documentation for these but i'm sure it should be possible to have one job build half of the tools and archive and upload those, another job build the next half by using "download-tool-archive", etc
i wanted to devise a way to distribute the build over multiple jobs
distribute is the wrong word
what i mean is basically: wrap up the build, transfer it to another job somehow, then continue the build
35 days is more than enough to build a full distribution
the other issue is that Lyre additionally simply runs out of available disk space lol
i should just use a build server/VPS and that's it
as is i just build a subset of Lyre packages, roughly equivalent to Arch's base-devel
โPropagate build artifactsโ
fair
wen xbbs?
Could you add an option to jinx to not have 3 source dirs
Like jinx --build-only or something
idk I think it would save space
though that isn't really the bulk of the issue, it's more about build artifacts
but i could do a build only option that does that, and on top of that, it thoroughly cleans any build artifact that isn't the final package
that should save a lot of space
Yeah that could work
i've been thinking a bit on how i can design the native APIs to keyronex
quite fond of the idea of exposing them with a portable distributed objects type system (in which objc messages are directly serialised and sent to the kernel)
this is not terribly fast because it involves message dispatch initially failing locally, a special object being assembled from the stack frame, and then serialising that object (containing the message target, selector, and all arguments) and sending it to the kernel
where the data must be deserialised into an invocation object and then the stack frame reconstructed programmatically and invoked
i fear ObjFW supports neither the automatic creation of that object nor the corresponding dispatch of the invocation object. neither of which are trivial to do
this is actually quite a fun little project in two halves: 1. write a function which doesn't know how it's been called; it must ask the runtime to provide it with a string representation of the return and argument types that it was probably called with, and it must use that information to create a structure containing each of these; 2. write a function that is called with such a structure + a function pointer and which calls the function pointed to, unpacking the arguments structure and packing the return value into that structure
i have an initial working prototype for up to 6 integral arguments and an integral or void return. trying to get it to work with struct returns - failing - i need to take a closer look at the system v abi spec
struct return ruins this whole thing
gcc isn't designed for objfw's runtime and doesn't call a special objc_msg_lookup_stret() function objfw provides. so the forwarding handler function, which is returned from objc_msg_lookup{_stret}(), is the handler for non-struct returns. and there's no way to proceed that i can think of from here
because now, thank you very much to the amd64 sys v abi, the argument order is all fucked up
since a pointer to the struct (to be returned into) is passed as a pseudo argument in %rdi
i'm just going to limit it to the simple case for now: integral/object arguments and return types only. it's now time to pick or create an encoding format
why not only use clang
i don't want to lash myself to the mast of one compiler
currently working out exactly what functionality will be available for kernel endpoints of this new IPC mechanism. i am probably going to rule out some fancy things like this:
- (void) iterateSomethingWithCallback:(function/block pointer or delegate)delegate;
such things would need to either run in a separate thread, or they would need a lot of new kernel infrastructure to do (either support for a 'syscall -> kernel code -> upcall user code' path, or some sort of coroutine facility to eliminate that)
GCC objc support is rwlly cringe though
iain sandoe has blocks on the way
and clang as explicit lit ๐ฅ๐ฅ๐ฅ objfw support
i have been informed of this directly by a gcc developer, arsen
Actually? Damnnnnn
Thatโs based af ngl
What else is missing from GCC?
lightweight generics, @() autoboxing support for numbers etc, probably quite a bit more
https://github.com/opensourcedoc/objcheck you might find this useful btw
GCC still doesnโt support generics? Damn
objfw falls back to id in this case
iain is doing a lot of gcc stuff at once huh 
Aye OF_GENERIC right?
he's also working on statement expr support for coroutines in c++
currently ({ if (...) co_return ...; }) causes an ICE :^)
i resisted using those statement expressions for a long time but i finally started using them last year and i love them now
so useful
yeah we use them for FRG_TRY and FRG_CO_TRY in frigg for frg::expected
so you can do FRG_TRY(thing_that_may_fail()) to just bubble the error up
but unfortunately, FRG_CO_TRY doesn't work on gcc so we can't compile managarm-{kernel,system} with gcc
ikr! its the one reason why I refuse to suport MSVC
do i have any use for BPF? i'm thinking of implementing a BPF JIT for the fun of it
in the meantime i've made good progress on the new high-level IPC mechanism:
the principle of it is that special proxy objects have a special handler for messages not understood, such that they retrieve the type encoding of the invoked selector from the objc runtime, which is sufficient information to identify which registers and positions in the stack frame contain arguments of which type, and what type of thing should be returned, and the arguments are all then serialised and sent to the server, which can then deserialise the same to manually construct the stack frame to invoke the method, and the same principle is used for the return value to be serialised and sent back to the client
are you doing a microkernel?
wait nvm
i have really gotten into the mood to implement (e)BPF soon
i have no use for it yet other than (and this is a stretch and really quite unnecessary) filtering udhcp's AF_PACKET socket so that non-DHCP packets are discarded
but i like implementing interpreters and JITs and this is a perfect excuse for that
there is a leak of between 4-10 pages on process exit
i need to find out where, really irritating
i have had no luck identifying the leaks yet. i am half-tempted to implement a mechanism whereby i can log every single allocation and free and perhaps send that data through virtio-vsock to some script on the host that can identify what was freed and what wasn't
frigg's slab allocator has the logging part and in managarm we do hook it up to a ring buffer that ends up getting dumped to a file over virtio-console 
including a tool that processes the output and does things like piping the stack traces through addr2line
the tool could probably be useful regardless, the format it takes in is a simple binary containing a bunch of records that look like this: ```
'a' for allocation / 'f' for free
64-bit pointer (result of allocation, memory being freed on free)
64-bit size (only on allocation)
up to 12 stack trace entries (as 64-bit pointers)
the 0xA5A5A5A5A5A5A5A5 64-bit constant
i'm glad i'm not the first to try it
i think it could actually be very useful
I think it's VERY useful
i've not confirmed this but i've just remembered
as part of testing the VFS i wasn't freeing unreferenced vnodes
probably at least a part of the leaking is the fact that vnodes are not currently freed, simply invalidated so i can detect errors
some of the likely culprits to the memory leak
now i'll need to add stack traces to the debug output and parse those to figure out what these are
excluding bufctls a typical invocation and exit of bash --version, after it's already been started once to fill the page cache, causes the leak of 95 allocations
memoy leuwuk
now i can find the leaks
among the leaked: a file, strings, epoll results structure (created by poll()-to-epoll() translator), but most interesting of all, vm_anon_t's
vm_anon_ts weren't being leaked, i can confirm after adding a pid field to the debug log, they were just getting dirtied first in the parent bash, after fork cow, before the child dirtied them. (i think)
following a round of fixes i now leak only about 1 page per process invocation
nice investigation, it's always interesting to read these things ๐
do have any idea where that one page is being leaked?
"about" 1 page?
i think it's the posix process or part of it
yes, sometimes there's 2 fewer pages, sometimes zero, sometimes 1, sometimes 1 or 2 more
so probably slabs getting filled or emptied
could you write every single kalloc to a file
with a backtrace
then simply find who allocated that page
to a file is in e9 or serial
or a buffer you can read with lldb whatever
this is roughly what i am doing and i threw together a simple parser for the output too
i think i've identified the last two leaks
- large slab bufctls not freed when a slab becomes empty and will be freed 2. refcounting around exited posix processes broken (the posix process appears to never be freed)
with these fixed, it's now time to consider other leaks and omitted features
signal handling is very incomplete (only a few syscalls are prepared to handle premature wakeup by signals.) tcp sockets leak memory because the socket transmit buffer implementation is not complete. unix domain sockets are only available for stream and not datagram sockets. there is a great deal to do
and i want to implement (e)BPF for the banter
having a look at some of the outstanding problems of keyronex. i built udhcpc without modification and it works, but there are some issues and resource/memory leaks - the first stinker is that there's no PF_UNIX socketpair so the udhcpc's mechanism for dealing with signals is broken (it uses the self-pipe trick but with a unix socket for whatever reason); unix sockets are leaked (there is confusion around reference counting of them, and though it's not applicable in this instance, i want to clean up the treatment of bind); and SIGPIPE isn't operational for them
__ensure(dir->__ent_next <= dir->__ent_limit) failed```
this is new - let's see what happened
i see what it is now: difference in alignment provided in 9p2000.L replies; the 9p2000.L readdir replies actually match the format of linux abi dirents, so i had once simply had them directly copied into the buffer, but later i instead explicitly copied them out one-at-a-time, only i align more aggressively, and hence overshot the buffer
stupid twat
wtf
this is impossible, and also surprising to see in what i assume to be the most robust part of the kernel
luckily you had an assertion
setting and reading these variables is protected under the big dispatcher lock so i don't know how on earth this could happen
yeah, i am exceptionally paranoid about things
i really wish there were a "who wrote to this address" function in the debugger so that i would have the necessary ingredients to solve problems like this
(this is the first i've seen this; i've been trying to solve bugs which Links browser is revealing)
there is
gdb has watch
it doesnt work well at all
I've used it before when something was writing to an address and I didn't know what
worked for me
who wrote to address X is useful in general imo
watch (char[100]) *addr will watch the range [addr, addr+100)
qemu record & replay in reverse
i can actually do it for the kernel by abusing the asan, which i have done before to solve tricky issues
oh thats true
i just arrange for its hooks to log the accesses instead of testing them
this feature is often spoken about but i don't think it's real
i spent 2 days trying to figure out how to do it
it didn't make a lick of sense - you need to have some disk device passed to qemu for reasons completely beyond my understanding
try watch
to record the state of them afaik
what i don't understand is why such a baroque interface instead of something dead simple like
-trace-to file.trace
unfortunately not useful in this case, i tripped this bug just once in about 100 runs
so a race condition of some sort?
seems likely, i am just trying to determine how it could be raced to
i just had a look and all the sites that modify it + this site that reads it are under the big dispatcher lock
so i can't rule out the possibility of something trampling on it which shouldn't have
a qemu write trace would be very useful
qemu should add more debugging features outside of gdb
this still remains my favourite debug feature of keyronex
i don't know how people live without describing what their threads are waiting on so they can swiftly identify deadlocks
Mm that is a nice feature
omw to add that
(steal that)
i have a rough idea about what might be going on with both the earlier bug and a new bug
the new bug is an amap (anonymous map, the structure describing anonymous memory; it contains a 3-level table of anonymous memory descriptors) becoming zeroed out randomly
i have had a look and found some instances of the slab allocator returning an already-allocated pointer and i think i must be double-freeing somewhere
exciting
frigg's slab allocator debug trace has been quite useful to me, you could probably add some sort of trace to detect leaks and double frees to yours
i have one, but i need to speed it up, i can only turn it on for limited times
i need a fast and special-purpose driver for virtio-console for it
why not ivhsmem
68k ports are back on the agenda
initially qemu's 'virt68k' machine then Amigas with 040s/060s
progress
nice
cheers, progress continues to be made
i have done away with the dependence on page table size = page size. instead they are allocated in groups of page-size (this was @neat steppe's simple suggestion. it allows for page table page-out to remain viable; if i allocated them from slabs instead, for example, i would have a single page be made up of potentially 16 page tables from different processes; much less likely that this would ever then be a candidate for pageout, since in each process the number of live PTEs in the pagetable forming a part of that page would have to reach 0)
this solution will work great unless and until someone shows me an architecture with page tables not of a power-of-2 size
very nice, almost makes me want to start a new project :^)
what sort of project might it be?
some new os project, mainly so i can get to write the kernel from ground up with all the knowledge i gained between now and when i last worked on my own os project :^)
do it, i'd love to see how it evolves
i would love to start a project like this as well, but i have no idea how to plan the project properly so i don't code myself into a corner and have to rewrite a substantial part of it... i also don't know how to design a good interrupt and io subsystems so i might want to hold off until i'm more knowledgable in these areas ๐ in the mean time, any tips on the first part?
for the first, i have a feeling it might literally just be a matter of discipline and planning
when i am lite on those i end up making a mess
it's much harder when you are on your own and don't have a team to discuss things with and get code review
why WOULD you allocate a page table from the slab
anyone tackled virtio-gpu yet for the simple case (framebuffer)? i'm sure i read before it's fairly straightforward. (i am using the goldfish serial port for now for output)
because on the 68040 they are not page-sized
the root level and middle level are 512 bytes long (128 entries each), the bottom level either 256 bytes (for 4kib pages) or 128 bytes (for 8kib pages)
does virtio-gpu even do framebuffers? i thought its just hw accel
it has both a 2d and a 3d mode
Interesting
Yeah I've done it, that's about as far as I got and moved on to other things, but the gpu-specific parts are very simple if you already have a way to manage the transport and general virtio init
https://github.com/DeanoBurrito/northport/blob/master/kernel/drivers/builtin/VirtioGpu.cpp#L267 here's a naive implementation.
cheers, i do have generic virtio facilities already so it hopefully won't be much work to adapt them
i do need to adapt them to virtio-mmio first and sort out endianness
yeah non little endian machines are something i'm dreading one day
virtio over mmio isnt too bad
I was only able to get qemu to provide legacy interface devices rather than revision one devices though
but it only really means your 3 buffer areas must be physicall contiguous
and I didnt really spend much time figuring out if it was a config error on my end
i saw a commit in 2019 introducing modern interface to qemu's virtio-mmio
possible skill issue then
it's nondefault in any case
it seems that it might not be a user-configurable option
for (i = 0; i < 128; i++) {
dev = qdev_new(TYPE_VIRTIO_MMIO);
qdev_prop_set_bit(dev, "force-legacy", false);
code from the m68k "virt" platform
you could try this argument i saw: -global virtio-mmio.force-legacy=false
ooh I'll make a note to try that
@slow agate you might be able to help here
https://gitlab.redox-os.org/redox-os/drivers/-/tree/master/virtio-gpud here is the one I have written
cheers, now i have some references to go by
getting virtio-mmio up and running on 68k is proving more obnoxious than expected
qemu-system-m68k for some reason appears to expect that when you write to VIRTIO_MMIO_QUEUE_etc registers that you write a guest-endian value (big in this case) rather than a little-endian one
i tried otherwise but it didn't work; i am going to have to be dead careful when i code anything here to make sure that i pay mind to what qemu expects. hopefully the queue address stuff is the only exception to the principle of little-endian everywhere in virtio
here's the managarm virtio-gpu driver if you need more references
https://github.com/managarm/managarm/tree/master/drivers/gfx/virtio/src
cheers, i'll add it to the list
finally managed - i think, i don't want to tempt fate - to untangle what qemu wants to be in guest-endian and what in little-endian. so now i can actually submit commands
it looks like getting to the actually framebuffer from here should be straightforward but it's time for a tea break anyway
great
nice
cheers. now that i know it works, it's time to iop-ify the driver
i always enjoy bringing back the terminal
no VM statistics as there's only a minimal VMM up and running in this port so far
was the netascale logo always greenish?
i remember it being blue
dark blue rather
oh, that's funny
i must've made an endianness mistake
it is dark blue on amd64
i thought my eyes were playing tricks on me ๐
also, are you planning on upstreaming this port?
i'd love to see how people approach multi-arch kernels
eventually yes, i am using it as an opportunity to experiment with some refactoring as well
There are other portable kernels u can also take a peek at in the meantime
This is extremely neat btw
i see, i started writing my new kernel yesterday and im having a little trouble planning out the arch specific things, not that it has to be right the first time but id like to have some level of organization from the start
i am planning to add a third arch as well, which might be aarch64 or even riscv64
yeah, what i meant is id like to see more multi-arch kernels :^)
cheers, you will find some other stuff in the pipeline even neater. i am experimenting with a working set model approach to vm again
i will try to get the code into a fit state to at least publish on a branch as soon as i can then
thanks ๐ in the meantime ill do what will said and look at how other people deal with it :^)
My kernel is multi arch if you consider printing hello world on riscv and arm multi arch 
one thing that is quite worth thinking about is interrupts, i previously assumed there was a concept of IRQ number or rather GSI number but on virt68k it's different, i have instead seven hardware interrupt levels and 6 PICs. so i am moving to having an object which designates an interrupt source and with which you can do things like mask or unmask it, and associate it with a handler
I have a HAL layer which manages things like architecture initialization, timers, interrupts and modifying pagetables
i have broadly the same
fyi this isn't what the HAL layer is in NT in case that's where u got the inspiration from
it abstracts the platform, not the architecture
the architecture specific code is in the kernel (and is simply separated out into arch specific subdirectories in each kernel component) since a particular kernel build is arch specific by nature anyway
what the HAL contains is platform specific stuff that varies across different platforms of same architecture
i make some assumptions which i may need to change in the future, e.g. that a platform has a facility for providing a timer for each core and that i can get that up and running trivially (because DeviceKit initialisation has to happen in true, non-idle-thread context now since it involves sleeping)
this isn't really a thing that happens on the PC but it was common with stuff like old workstations and also ARM now
I think that assumption will hold across any SMP machine you can get your hands on, and you can easily simulate it if it doesn't
by sending an IPI from the one core that gets the timer interrupt
to the other ones
(& if it doesn't have an IPI mechanism it's shit)
if it doesn't have an IPI mechanism i give up altogether on that platform
Yeah I don't know how you could implement a general purpose OS without one
even if it has something cool like a broadcast-tlb-flush instruction i still give up on such a platform because i like to do things like send an ipi to a core to preempt it when a thread suited to run on that core becomes ready
It's not
I just call it a HAL
It's a generic name not tied to NT
Think it is tied to NT
Never seen it used in any prior situation
All you really need though is per subcomponent arch specific directories
That's what I've been doing and it's been pretty clean so far
You could also put it all together in one directory per architecture but that's a bit too far removed from the context of where it's used for my taste
i dont remember if i ever asked this, or if i did whether i got an answer or not, but does anybody have any resources about interrupt management? id love to design everything in my kernel properly from the ground up and i feel like interrupt management wont be very complicated but it will let me implement core parts of the kernel along it
for this i think it's worth exploring how some actual kernels do it
there are two major items of interest i think
the first is the approach to prioritisation and suchlike and how scheduling fits in with interrupts
the second is the abstraction of interrupt sources so that drivers can be written generically
i guess interrupt management will be more complicated than i expected
i had a brief chat with #osdev irc about this at one point and they didn't care for interrupt prioritisation and instead they advocated for making every interrupt threaded, and certainly i can see that such an approach isn't totally without merit, but i have committed to the IPL concept and make extensive use of DPCs so it isn't for me
i wanted to go the dpc route because it seems fancy lol, and mainly because i dont really know any other approaches
i don't think it has to be very complicated, i think for example the core concept of interrupt priority is quite straightforward, it's slightly less so if you have to emulate it in software due to an inadequate platform
certainly much harder than having a semaphore and a worker thread but i feel like i can learn a lot and it will be challenging to write good code
that's what the consensus on #osdev irc seemed to be when i last discussed this with them
so its more of a โno need to trouble yourself with that for a hobby kernelโ argument
well, they actually argued against them entirely for any kernel
out of date concept, they said
fortunately i am also out of date now (you go out of date after the age of 25) so it's no problem for me
i hate clang-formatfformatting
// clang-format off to the rescue!
also before that i like to try and tame it with //
is your column limit like 70?
80
damn that is still pretty low
i find 120 to be a lot, but i dont hit it often
im often below 100, which is pretty good for me
but it may be because i have a high resolution screen + i use a small font size ๐
MANAGARM MANAGARM MANAGARM MANAGARM
fwiw I've taken a similar approach to what abbix describes: there's a collection of functions that abstract away platform specific stuff, and the rest of the kernel is built on top of those. I found the tricky part to be deciding exactly what I wanted those abstractions to be, but once in place its been pretty easy to maintain support for 2 archs. The real test will be adding a third ๐ .
It's basically just a set of common headers, and then each architecture can add its own implementation files to build with the rest of the kernel, depending on the target arch for that compilation.
So far I'm finding it better to abstract at a slightly higher level than I initially though, and then emulate any missing features for that arch in software where necessary. Not sure how that will work out long term though.
(sorry for ping, forgot to disable it)
nah its alright, thanks for your input!
and as you said i find the "what should go where" part the hardest so far
it might be worth starting work on two arches simultaneously to help clarify that
I completely agree with this
I found it way easier starting with 2 at the same time, then adding support for a second one later (my original approach)
i am thinking of throwing aarch64 into the equation here as well so that i am sure i design the right abstractions
haha same ๐
in fact i'll do it just as soon as i figure out how one pairs limine with, i suppose i'll go for qemu's aarch64 virt
i think tackling both the kernel design and learning a new architecture at the same time might be too much for me, idk what to do
do you know only amd64?
yeah unfortunately!
for all its jank, riscv is similar in a lot of ways to x86 (in the context of learning a new arch)
from what ive heard it has a lot of weird quirks
man the arm architecture reference manual is positively mastodontic
13 thousand pages
meanwhile i am happily perusing the nxp 68040 user's manual at 450 pages
keep in mind there's 2 distinct instruction sets + little and big endian versions ๐
I just have a arch directory with architecture specific stuff and the "hal" (collection of functions and classes that must be implemented)
yeah it's enormous
prior to armv9 it was only 9k pages
or at least i think it's because of armv9
because i do have older copies somewhere
well virtual memory doesn't differ that much from x86_64 in my experience, except that the virtual addresses are 1 bit larger (49 vs 48), with separate registers for page tables for the lower and higher halves, and that there are no mtrr equivalent to automagically set correct caching modes for mmio, and that at the bare minimum, the cpu does not do dirty bit or accessed bit tracking so you need to emulate it
i can also get those sweet sweet 16kib pages
true, it's more flexible, but you can also just configure it to be very similar to x86_64 (4k pages, 4 levels)
68040 also has separate registers for the higher and lower halves' page tables (and then it multiplies the number by two again, having separate tables for data v.s. instruction accesses - i don't know why this feature is present)
only reason i can think of is execute-only pages
i now have an aarch64 toolchain together and i figured out the magic incantations to launch qemu-system-aarch64 with the uefi firmware, and limine just worked (very nice)
now to familiarise myself with an architecture about which i know next-to-nothing
Isn't arm paging like super complex
Compared to x86
well it is very flexible, but after that the actual page tables are quite similar if you configure it to use 4k pages with 48 va bits
well theres a bunch of bits that if you get wrong doesn't work on real hw while working in qemu :^)
kvm helps a bit with finding those bugs but it doesn't catch all real hw problems still
iirc the hardware dirty bit on x86 isn't actually used by anything because it has some race conditions
and so it gets emulated so that it can be set with a spinlock held
ah huh
like they map the page read only initially and then use that to detect the first write rather than use the hw dirty bit
yeah that's what you're supposed to do on aarch64 as well afaik
for accessed bit emulation there is an access flag, that causes an access fault if unset in an entry (different fault compared to a non-present page)
iirc the race condition has to do with something like, you might check if the PTE has the dirty bit set, it's clear, then by the time the other core receives the IPI to flush the TLB entry for the page, it has dirtied the page
so you just miss the page being dirtied
built on aarch64. my first priority will be to implement the IPL mechanism, context switching, and the basic setting of page tables for mapping devices
this will be fun as i continue to know virtually nothing about aarch64
nice, are you using limine?
i am indeed
actually it traps from time to time in limine
i will collect some info about that next time i see it
i'd appreciate that
i don't think the aarch64 port has gotten any serious use so there may be some bugs lurking
i've never seen it trap in Limine myself
but it trapping from time to time makes me think it may have to do with KASLR?
yeah it being random makes me suspect it's something to do with kaslr
i've never had it happen to me personally, although i've had more runs where it fails because i'm working on it
so not a lot of chances for it to break where it should've worked
ig you can try running it over and over
ask yehuda for his kernel maybe, to better reproduce
in case it won't trigger otherwise
doing some investigations atm
i actually can't reproduce it without timeout=0
and even then quite rarely
ignore that
epic, i made qemu segfault
memory starting at 0x40000000 is reported usable - isn't that where qemu-system-aarch64 -M virt supplies the dtb?
without uefi yeah, with uefi im afraid you only get acpi
i can live with that
wait, you can't get the dtb using fw_cfg?
oh I guess fw_cfg isn't available through uefi
i mean its not reported via the standard mechanism (dtb guid in the system table or whatever it was)
yeah
oh fuck!
you find fw_cfg through the dtb
yeah no that's painful
so there's no way to do based fw_cfg shit if you use uefi
cant you find it via acpi?
also i wonder if theres a uefi setup option to enable the dtb in the tianocore for virt, since there is one in the uefi fw for the rpi4
i found it as QEMU0002
why is every manual to do with aarch64 so big and sparse
constantly directing me to another section or to another manual for details
i recommend getting a pdf viewer with fast search functionality :^)
god knows i need it
and so many names and terms
any progress wrt this supposed Limine bug?
no, i will try to investigate it at some point
whaat bug
limine explodes and kills your whole computer and your family and then the CPU gains sentience and it stands up with lasers and KABOOM hole in the wall, uh oh, super CPU just escaped the socket, and it goes around the city blowing stuff up until superman stops it
thatโs not a bug thatโs a feature
a synchronous exception at some address or other
i will try to induce it again some day
in the meantime i am trying a radical new approach to solving some very tricky bugs in the vmm
namely i am implementing it hosted, in a simulated form, with full access to all the tools that this places at my disposal
been there done that
https://gist.github.com/qookei/c6d342806d1d0f5cc499cc0daec541f9
would be nice to have a proper way to do this so we could write tests for various operations
great minds think alike
i haven't decided yet how thorough i want to be with this
i can certainly spawn a few pthreads to do 'reads' and 'writes' and add functionality to inspect the state of all the data structures to make sure they appear sound
god I get PTSD from that
i've got the code adapted to run hosted now
hooked up to mock platform ""mmu"" code
i can mock the keyronex async i/o mechanism as well with an elaborate system of threads and signals and then i can really put this thing to the test
should be able to try out all sorts of things, including out-of-pages conditions
That's quite useful. Is the code available anywhere? This is similar to something I'd been thinking about trying (one day), I wouldn't mind the reference.
i'll put it up on github shortly
the principle of it is quite simple
No rush, I am neck deep in vmm + page cache shenanigans right now. ๐
i shim the keyronex kernel functions that the vmm requires (spinlocks, mutexes, later i'll need to also shim the async i/o code which is required for file mmap and swap stuff)
thread-local variables provide an ipl, current pagetable, and current process pointer. as to the pages, right now they are an array of byte arrays. it works because the arch-specific MMU code provides functions like "get a virtual address at which i can read a given physical page" and how that's implemented is meant to be agnostic (so it could be done by a direct map, or by some kind of temporary mappings if i didn't want a direct map, or in this case the simulated arch mmu code just gives you the address of that page's data in the array)
mm ok this all sounds very similar to how I was going to approach it
were there any particular pain points you encountered?
I hope you doint mind a brief interview ๐
as for most of the metadata i just allocate that with system malloc so that valgrind/asan/whatever can deal with it (kernel wired heap has its own treatment anyway so i am not overly concerned about that, but i am more woried about making refcounting errors, inter-thread races, etc)
so far none, i decided to simplify matters by - rather than trying to use an elaborate system of mmaps and SIGSEGV handlers - defining an "access()" function to access memory
and the idea will be that i'll create threads that will call access() with various random addresses for either read/write at random times
that should impose some nice stress on the eviction functionality
interesting, so access() simulates a memory access and then you can wire that to your vmm's page fault handler?
well after checking the access is valid I guess
yeah, it simulates the MMU itself in that it walks the page tables and if it encounters an invalid pte at some point, or it's a write access but pte is not set writeable, it then calls vm_fault() to handle it
i haven't, i think they are robust and reliable
except for one in twenty times running links and browsing the web there is an apparent race which i still haven't figured out
it probably helps that there is a global spinlock protecting the underlying primitives of scheduling, synchronisation, etc
so there is less scope there for problems
that does help simplify things
in the future i will loosen the locking on it with fun new things like per-core ready queue locks, since that's the easy case. then the actual reschedule code will no longer contend on the global lock. i reckon that alone should be sufficient to get reasonable scalability
I moved to that in the early days, but I remember it being a huge change for boot times
and a huge week of debugging
well worth it though imo
the futex is the main thing exposed to userland for synchronisation, and that only relies on the global scheduling lock in the contended case
in any case i think being able to test components in userland is a huge boon
it's how i made my allocators robust
likewise (re: testing allocators in userspace)
I've had this pipedream of slowly moving parts of my kernel into standalone userspace programs
starting with the vmm and scheduler (and I guess the device tree parser already exists standalone
)
thanks for the insight
no worries, and best of luck with your endeavours
i like northport, it's clean and well-architected based on what i've read of its code
nice namedrop ๐ I appreciate that
I'm well into "head down and research" territory right now, so my regular updates to it have suffered a bit, but I do plan on a third iteration.
you guys are catching up to me in the vmm shit
I've been stuck ankle deep in a swamp for months straight half due to low motivation and half due to compiler design being evil
hardly, my vmm/kernel is still relatively juvinile.
All of our kernels are juvenile
i'm hoping to do it really really properly this time
trying to completely tease out all potential problems
make changes that will allow me to do all the fun things
and adopting the working set model
maybe you can offer your opinion on something
there is no gain from paging the metadata of a memory object associated with a regular file vnode, there is obvious gain from paging the metadata of shared anonymous memory objects though
i use a three-level table for those
in servicing a page fault i think i'd really want to pin each level of these as i retrieve it, but that has locking implications
it would be foul to keep the map mutex (which protects the address space layout of a process) locked during the process, which would be the only way to ensure that the pages i pin into memory for that anonymous object as i potentially page them back in remain meaningful
i could, instead of pinning them immediately, instead just place them into the process' working set list, and hope that i don't end up in a cycle of chaos with them getting paged out before the page fault handler gets resumed after i/o
vms/nt using linear tables here makes much sense
suppose i could just keep a big "ongoing fault state" structure, including space for storing what anonymous memory object we faulted on + wired references to the needful pages making up its tables, and store the needful in there, and then test my assumptions when it's time
I used to use a 2 level table when the shared memory section was more than 8MB and would use a linear table if it was fewer
However that was prior to me having paged kernel memory
And I realized none of my shared memory sections were anywhere near big enough to warrant anything special
So I ended up just making it be a linear table appended directly to the end of the section object in memory
(Will obv have to change if they become extendable)
But basically I don't have to deal with that issue since I just have a small nonpaged linear array
If I did then I'd probably just do it the same way I do page table pinning
I might even have a special paged pool just for allocating chunks of prototype page tables
It'd be separated so that the pinning doesn't overlap with the other allocations on the same page too badly
what i have planned for process private pagetable pinning is based on an idea that netbsd might adopat at some point in the future
Also I wasn't worried about people exhausting nonpaged pool with huge section objects because obviously quota is charged for it
i call it the refault state struct
and it can hold a reference to each level of the process' page tables for a given virtual address. and until dealing with huge pages, or even large pages, this can be done without any harm. so as we descend the tree, if we need to swap in a page, we can store a reference to it in the refault state for safe keeping before we begin a refault
doing this for shared anonymous memory objects, now that i think of it, should only be a little more effort
it should be sufficient to verify at each stage whether the value in the refault state struct is the expected value
is it the same anonymous object (top-level page same?) is the 2nd level the same? and so on
that's poorly explained but i am tired and unwell
mock pmap for hosted development of the vmm is now operating
now i can experiment with all kinds of fun things
@neat steppe unmapping (or protecting) regions really becomes a nasty business with pageable page tables
why not just page them in?
it's the permitting that which is annoying
unmapping becomes something that can block, and that means it has to release spinlocks when it blocks, but we have to make sure we keep everything sound
in principle we could hold the address space map mutex across the pagein, but then if we needed to evict pages from that map, we are in a bit of trouble as we can't
anyway i am not planning to support that just yet anyway, important thing for now is to fix up refcounting, accounting, and locking, and to move to process-local page replacement
i think i'd like to implement a generic framework for resource accounting
should be hierarchically organisable and flexibly implemented, so that it might also be applied to e.g. cpu time, disk time, etc
isnt object manager doing that ?
or should do
it isn't in keyronex, nor is it in NT as fara s i know
ah, so in this case resources != objects
in keyronex it played a minimal role so far as mostly a place for reference counting logic to be shared in, in the keyronex branch i have been working on with m68k and aarch64 ports, which is not nearly up to feature parity with the main branch, it replaces the posix fds
actually in NT object manager probably does have generic code to charge processes for the objects they use
i'm not overly familiar with NT but this would make good sense
do a risc-v port
why riscv?
i'm sure i would get further faster with risc-v than with aarch64 as there can't possibly be as many manual pages to wade through
risc-v should be a bit easier
i will certainly look into it once i've made a bit of further progress with the arch-independent work
https://github.com/Keyronex/Keyronex-lite/blob/master/docs/notes/vm.rst this document is the start of some design docs for the VMM improvements to be applied to keyronex
hm, says page not found
these were going to just be rough notes, but i think they might actually end up being what i work to when i implement this
i am crap at writing presentable rough notes, i feel the need t get in depth and at that point it's better to just do it thoroughly
i should really figure out what's going on here
No \r?
[Insert Megamind pic here]
Managleg give you the response you need \r when you do an \n. \n\r
i think he:s talking about figuring out the fault itself
can you show your qrmu flags that lead to this? ill try reproducing it
certainly, it's qemu-system-aarch64 -M virt,gic-version=max \ -bios /usr/share/qemu-efi-aarch64/QEMU_EFI.fd \ -boot menu=on,splash-time=0 \ -cpu cortex-a72 \ -smp 10 \ -device ramfb \ -cdrom build/virt-aarch64/barebones.iso \ -serial stdio -s
ty
keyronex/amiga68k loader is working again
unfortunately keyronex/amiga68k doesn't do anything interesting yet, but it does submit some copper lists and draws a cursor
nice
bitmap?
yeah, fetch it in psf format from https://github.com/NetaScale/SCAL-UX/raw/main/kernel-3/dev/fbterm/nbsdbold.psfu
ty
Thanks
for the font or for drawing it on amiga?
For the font
glad to hear it's so well regarded
this has been a pleasant surprise
i will not tempt fate by speaking too certainly but it looks like i succesfully divided the virt68k specific code from the generically m68k when i wrote it
if you wanna test irl tell me
I got an amiga for porting ironclad, that I can use for testing your stuff
i have one too, but an extra place to test would be fun
keep me up to date on ironclad porting to amiga for that matter
right now i'm trying to determine whether FS-UAE is being a prick or whether i've messed up my copper lists or something:
it only happens with one build of fs-uae so i've no idea
i want to try on my amiga 4000 winuae config
which has amigaos 3.9 installed
and a cybervision 3d gpu
which looks much better than stock aga
i just tried it under winuae on my windows laptop as an additional test and it did run:
but it's very finicky
cool
the loader is a mess, i'll need to implement a serial driver as i can't provide any output for most of the load process because i need to turn off interrupts, enter supervisor state etc which is incompatible with further printing before keyronex takes over
right now im working on executable signing with some consulting from n00by, after that I will do riscv, then amiga, then the JVM thing
what the fuck
ironclad java
looking forward to it
you have a stock A1000 right?
just spent 2 hours getting agitated about keyronex not working on an (emulated) 68060
turns out that it's helpful to check whether amigaos exec is describing a memory region as being ram (and not rom) before you try to use it
basic functionality of the amiga 68k port is now seemingly working
threads are scheduled, timers are managed, all the stuff i did for virt68k has largely carried over, and if anything it's been simpler for the amiga
there is still work to do, 68040 requires your bus fault handler to manually complete write-backs (it pushes a 30-word stack frame with state to help you figure that out) , there must be drivers, the loader is messy and falls apart with slightly unusual memory maps (which is common on amigas), and bringing up userland etc
but overall i am happy with how things have progressed so far on this port
first attempt to run keyronex/amiga on actual hardware
it goes as far as printing this bracket
Exhilarating
it looks like i can print constant strings but it dies on trying to print at least string and integral (?any printf specifier) parameters
maybe you ran into a 68k varargs regression
with your compiler
im sure someone would have found and fixed that by now
but then again its 68k
wait
it works on an emulator so thats not the problem
i could use some debugging facilities
let's see if i can install a fresh IVT nice and early, and manually print out the faulting PC
assuming an exception is generated somewhere and i am not having to chase some unfunny kind of problem where the PC goes walkabouts
thank you
i can work with that
that's an illegal instruction exception
fd000322: 000c .short 0x000c
this thing is very troubling
it fails in a seemingly nondeterministic way
and sometimes it even succeeds in the printfs but leaves the specifiers unexpanded
i think it's that the 68060 lacks certain instructions from earlier 68ks
presumably the emulators just implement those
i seem to be consistently ending up at strange PCs
but just to be as annoying as possible, it actually worked exactly once
i mask them immediately for the purposes of this
then what could it bee
--- out2 2023-08-26 20:02:08.741696209 +0100
+++ out 2023-08-26 20:08:45.571670175 +0100
@@ -440,6 +440,7 @@
fd0003ec: 2140 0006 movel %d0,%a0@(6)
while ((*cur >= '0') && (*cur <= '9')) {
fd0003f0: 206e fffc moveal %fp@(-4),%a0
+BAD! 3f2
fd0003f4: 1010 moveb %a0@,%d0
fd0003f6: 0c00 002f cmpib #47,%d0
fd0003fa: 6f0c bles fd000408 <npf_parse_format_spec+0x166>
@@ -472,10 +473,12 @@
fd000434: 6610 bnes fd000446 <npf_parse_format_spec+0x1a4>
out_spec->prec_opt = NPF_FMT_SPEC_OPT_STAR;
fd000436: 206e 000c moveal %fp@(12),%a0
+BAD! 438
fd00043a: 7002 moveq #2,%d0
fd00043c: 2140 000c movel %d0,%a0@(12)
++cur;
fd000440: 52ae fffc addql #1,%fp@(-4)
+BAD! 442
fd000444: 6070 bras fd0004b6 <npf_parse_format_spec+0x214>
} else {
if (*cur == '-') {
@@ -533,10 +536,12 @@
fd0004b6: 70ff moveq #-1,%d0
fd0004b8: 2d40 fff8 movel %d0,%fp@(-8)
out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_NONE;
+BAD! 4ba
fd0004bc: 206e 000c moveal %fp@(12),%a0
fd0004c0: 42a8 0014 clrl %a0@(20)
switch (*cur++) { // Length modifier
fd0004c4: 202e fffc movel %fp@(-4),%d0
+BAD! 4c6
fd0004c8: 2200 movel %d0,%d1
fd0004ca: 5281 addql #1,%d1
fd0004cc: 2d41 fffc movel %d1,%fp@(-4)
@@ -548,6 +553,7 @@
fd0004da: 7412 moveq #18,%d2
fd0004dc: b480 cmpl %d0,%d2
fd0004de: 6500 00a8 bcsw fd000588 <npf_parse_format_spec+0x2e6>
+BAD! 4e0
fd0004e2: d080 addl %d0,%d0
fd0004e4: 2040 moveal %d0,%a0
fd0004e6: d1fc fd00 04f2 addal #-50330382,%a0
@@ -567,6 +573,7 @@
fd00051e: 2140 0014 movel %d0,%a0@(20)
if (*cur == 'h') {
fd000522: 206e fffc moveal %fp@(-4),%a0
+BAD! 524
some of the sites i have observed bad PCs at
(if i say bad! + fragment of an address, thats the pc i observed a fault at)
now the interesting part is that they almost all involve %fp-indirect addressing with displacement
i hit it with a big hammer
it still doesn't work 100% of the time
sometimes a bus error happens trying to access an address which should have been mapped by the loader
anyway
bus error dont necessarily mean not mapped
just that theres nothing on the bus for the corresponding physical address
or that you used it wrong
i need to check the exception frame for it
i think at least kprintf works now which makes this much less hassle to deal with
i had been mapping pages in which page tables lived with copy-back cache mode for some reason
and the 68060 doesn't enjoy that
then I had to install the M68060 Software Package into my kernel
it's a set of wrappers for your exception handlers to do things which ought be done on 68060s
i'm happy with this for now, it's been a good day's work
yeah that is really good progress
cheers, i am just glad i had that brainwave about the cache mode on pages mapping the page tables
i studied the 68040 manual in depth but only skimmed the 68060 manual but i did vaguely remember reading that unlike the 040 it doesn't consult the data cache when walking page tables
CRTs look so good. retvrn
I miss them
they were working on nicer display tech to try and preserve some of their nice properties up until the great recession
something i've touched while working on the amiga port has broken virtio-gpu on virt-m68k apparently
ugh
repeated notification of the same completion
turned out to be as simple as forgetting to initialise allocated kernel wired heap; previously that would zero, now it doesn't
amazing work, does this require a MMU?
cheers, it does and only supports the 68040 or 060's builtin MMU at present
i will do the 030 mmu at some point when i can be bothered with the complexity + lack of a good debugger (qemu doesn't emulate it)
for now i am considering doing a driver for the scsi controller in my a2000 (ncr 53c710 based) but i can't figure out how to configure FS-UAE for it. options have inconsistent naming conventions, it's a mess
no, i think i'll adapt the virtio-disk driver to the changes in devicekit, and then do an ext2 driver. keyronex has been in the odd position of having 9p and fuse but no disk FS driver yet, and this will be worth doing to reset some compasses that might have lost their bearings
i've implemented a monstrosity, the Grand Unified Buffer Cache (GRUBC for short)
lol
this provides a buffer cache interface and buffer cache level of control over when individual blocks get written back while participating in the system page replacement policy
using the partition device's page cache is unacceptable because FS block size < page size leads to problems like the partition device's page cache caching pages which contain metadata (desired in this case) and file data (undesired, produces incoherence with respect to the page cache of those block's owning files), and there is no sensible way to represent the ext2 metadata blocks in the form of a contiguous address space composed only of metadata blocks (as you can do for files)
i am thinking of implementing the StorPort API for storage drivers. it might be a fun lark
i had a look at some typical storport drivers and they tend to only use these 20 or so functions
all the high speed modern ones at least, it seems
The MS page that was the top Google result made it sound like it was for things like fibrechannel
and some of the older ones against a previous, similar API called scsiport
But a couple of results down was this: https://www.intel.com/content/www/us/en/download/15630/intel-ahci-storport-miniport-driver-64-bit.html
Intelยฎ AHCI Storport Miniport driver for Consumer/SOHO Storage for Windows 2003* and Windows 2008* 64-bit
now some of these functions are really multipurpose ones so there's a larger API surface than appears at first glance, but i'm sure this wouldn't be a huge amount of work to implement the basics of
oh man
This repository holds the source code to NetAdpaterCx.sys.
NetAdapterCx.sys ships with Windows, so you don't need to compile it yourself. However, you can use this reference source code to debug your own NIC driver, and to learn how NetAdapter works.While we're proud of our API documentation, we know that even the best docs can't always answer every question you might have. Sometimes, you just have to refer to the source code. We've published this code so you can be more productive while developing your own NIC driver. Our aim is to make the inner workings of NetAdapterCx as transparent as possible.
After you get done with storport ๐
now that could be another fun venture
I assume you've already seen these, but in case you haven't: https://github.com/microsoft/Windows-driver-samples/tree/main/storage/miniports
cheers, i hadn't seen that subfolder
that ahci driver will be a nice test
meanwhile i've discovered gcc lets you annotate a function with an ms_abi or sysv_abi attribute which will save me having to write asm shims
i've thrown together a coff/pe loader and successfully loaded viostor; now i'm going to try to figure out how one makes a debug build of it so i can start exploring further
nice
making progress with this storport shim
this is atop a testbed kernel i threw together, i need to bring it over to keyronex now before i can get much further

allegedly the viostor driver for windows is set up and ready to go
i expected it to ask more of me. i suppose it will when i ask it to start doing i/o
cheers, it's not meant to be blue but such is life
maybe an endian mixup since i reprogrammed the console for m68k
turns out viostor.sys doesn't actually do much if you don't path a registry path argument to its DriverEntry routine, so I have done the needful there and now it's calling that mastodontic StorPortNotification function with all sorts of requests
first Srb submitted and apparently worked - it just read the device ID
it's notifying the the virtqueue and getting no further, let's see if i can figure out what's going on
that was fast
it's not yet integrated with the interrupt infrastructure so i have to wait a moment and then check my i/o occurred
but i am happily surprised to see this so soon
roughly how many lines of code did you have to write to make it this far?
about 700 on shims, 200 on a PE loader
notbad.jpeg
it's the bare minimum to get any I/O done but i'm keen to expand this now
keyronex will become the no. #1 hobby OS for disk drivers
I'm honestly surprised more people don't take this route
if someone suggested it to me before i would've thought it was an outlandish idea which is impractical, after all, even reactos struggle with windows drivers
but o3one os did it with storport's predecessor, scsiport, and that got me thinking
hell yeah
Holy shit, nice
currently in the midst of refactoring this storport driver layer, which has been until now an experimental toy, into a proper component of keyronex
and indeed i think i will actually use this as the standard way to implement SCSI hba, and even other disk HBA, drivers for keyronex
windows virtio-scsi driver is now working
windows supports virtio-scsi?
if you install the write drivers, yes
ah
i'm going to try to get the refactoring branch organised and ready to put on github over the next few days
i know at least 2 people are interested in the storport shim so i owe them to do it
I'm eager to take a look
I want to investigate how feasible it would be to do other Windows driver interfaces, mostly for WiFi tbh
what's a shim?
oh, so something similiar to what wine does, very cool
i've been trying to do a little work on making the aarch64 port do anything interesting but jeeze is aarch64 one of the most motivation-wrecking platforms i have ever worked with
i can't put my finger on what it is
maybe their sparse and insipid manuals
don't worry, it's been like 2 years and managarm still does not boot into weston on the pi4 :^)
although it does boot into weston in qemu at least
do you find it gives you some kind of feeling of overload working with it?
it might be all the new acronyms
well it was a bit overwhelming initially, considering that when i started working on the port i had no prior experience with aarch64
on real hw the main problem currently is usb stuff and sdhci, apart from that the road to weston should be simple
that's definitely the big thing for me, i have never touched aarch64 before in my life
while me and the 68k and x86 go way back
it took me quite a while to figure out the page tables too
including caching modes
i remember having a lot of trouble getting mmio (to talk to the uart) working on a real pi4 initially
the documentation is spread out all over the place, i found in one document the format of non-terminal page tables and another hadthe format of terminal page tables' PTEs!
and way later i had a weird bug due to a one character typo that caused smp to be borked in a really weird way (reads to a per-cpu register seemed to arrive from a random cpu and not the one that actually issued the read, because i accidentally mapped it in with nGRE and not nGnRE)
if you have hardware where you can run your aarch64 code in kvm i recommend doing so
it can help catch some things that qemu doesn't care about (like forgetting to enable caches, which breaks atomics)
yup it sucks
I've been working on aarch64 page mapping code for the past two days lol
good idea. I was planning on doing direct testing on baremetal, but that's probably a good smoke test to do first
unfortunately it won't reveal caching issues per se (picking the wrong memory type)
worst comes to worst I break out the JTAG ๐
i remember trying to get it working on my pi4 but couldn't
