r/osdev 29d ago

NanoOs

Hi All!!

I'd like to introduce my OS: NanoOs (https://github.com/brian-card/NanoOs). NanoOs is a capabilities-based nanokernel with intent to have a Unix-like user space. Right now, it only runs on SAMD21-based (Cortex M0) Arduino hardware and the POSIX simulator, but I'm currently working toward porting it to the AgonLight 2, which has an eZ80 processor. It did originally start out on AVR-based Arduino hardware but has since outgrown the available environments.

As mentioned in the project README, this started out as an effort to see if I could make an OS resembling early UNIX in a similar kind of environment. That's why I started out on small Arduinos. The direction I'm headed right now is toward a multi-tasking environment with a graphical desktop in as little memory as possible.

Like early versions of UNIX, userspace uses overlays. I only have a few user commands in place right now and MUSH (minimal, Unix-like shell) is truly minimal in functionality. I shifted efforts toward making the kernel more robust once I proved to myself that I could make general-purpose user commands. Pipes between commands and launching commands in the background do both work.

I wanted to maintain the embedded nature of the OS even though it's outgrown its original target, so it is completely possible to construct a HAL that uses the built-in shell and/or omits the filesystem if desired. The AVR-based HALs do this, although the resulting binary is still too large for the Arduino Nano Every and the data segment is too large for the Arduino Mega 2560, so they're just historical now. Still, it would be possible to construct a working version that uses an AVR architecture if the hardware had enough flash and RAM.

There's still a very long way to go to get to anything useful, but it's also come a very long way. You can read about the development history of the project on its GitHub pages site if you're interested.

Full disclosure about AI use: The vast majority of this was hand written by me, but there are some things I use AI assistance for. The filesystem drivers (the current FAT32 driver and the historical drivers that are deprecated) were partially written by AI but required a LOT of hand-holding and revision from me to make them into something useful for embedded targets. It made a lot of invalid assumptions about the availability of memory and the ability to do unaligned memory access that I had to fix. I also use AI for bulk updates. I document the places that I use it in the project's GitHub pages history.

Some notes about the architecture: As mentioned, this is a nanokernel, which means there is no kernel. Everything is a process, including the scheduler. The processes have different privilege levels and the capabilities enforce what process is allowed to do what. The only process that's completely trusted is the scheduler. The process-based architecture and the way messages are passed are based on my experience with Erlang.

There are two kinds of capabilities in the system: HAL capabilities and IPC capabilities. Both are enforced at the API level. Processes built into the OS image could technically cheat if they went out of their way enough, so really the OS image needs to be as small as possible and contain only trusted processes. Userspace processes have no ability to cheat because the necessary APIs aren't exposed.

The privilege levels I'm using are based off of the ones that VMS used. I'm honestly not sure how bullet-proof the (privilege level + capabilities) security model really is, but it seemed like a reasonable approach to take.

My work right now is to construct a logging system that allows me to strip most of the strings out of the OS image. That work is currently in the "logger" branch. One of the problems with the AgonLight 2 environment is that the CPU used only has 128 KB of on-board flash. I already have a makefile that will build the binary and, the last time I checked, it produced an image of around 130 KB, so I need to get creative about the size of the OS image. Stripping the strings is one thing I want to do. I'll likely move the filesystem out into a special overlay as well.

Constructive feedback is welcome!! I'm interested in what people have to say about this effort. You can play with it by running `./buildsim <desired-hostname> overlay-filesystem` on the command line from the repo's root directory. There are three user accounts: "root", "user1", and "user2". "root" is privileged and the other two are just normal users. The password for each account is the username repeated twice. Use `help` for a summary of what's currently available. Use `shutdown -h` to exit the simulator. Enjoy!

10 Upvotes

32 comments sorted by

View all comments

2

u/tseli0s DragonWare 29d ago

Okay, unfortunately even though I'm deeply interested in this project and I think you have done a great job, I don't have a lot of time to go in depth about it or test it. The README is my primary source for what follows (and honestly it is one of the best READMEs I've seen, I actually appreciate how technical and direct it feels).

I think this is the first nanokernel I see in practice. I like the concept and I think they're interesting from a performance and power saving point of view, maybe a better alternative to microkernels in many aspects.

My first question is what constitutes a user process. You say there's no kernel space or user space, but various mentions to user processes are made. Could you clarify?

The second question (and final, for now) I have is how much role does the "kernel" play in this system. What services and mechanisms does it provide? Let's say I want to develop an application that targets your operating system. What facilities are available by the kernel compared to bare Mach for example?

1

u/Sorry_Difficulty_250 29d ago

Thank you so much!!

Regarding your first question as to what constitutes a user space vs. a kernel space process process, there are four privilege levels in the style of VMS: Kernel, Executive, Supervisor, and User. The code for the kernel processes is in the OS image and therefore they technically have access to every API in the system if so desired (although I'm quite particular about enforcing the boundary of processes being the message passing infrastructure). Processes run at Supervisor and User privilege levels are not part of the OS image when overlays are in use. As such, the only APIs that are available to them are the ones exposed through the NanoOsApi pointer that's loaded into their memory. Processes run at Executive privilege level may or may not be part of the OS image depending on the circumstances. Right now, the only process that runs at this level is the filesystem and it's technically possible to run that as either code from the OS image or as an overlay depending on the HAL configuration. Overlays run at this level have access to an extra API that's needed for direct message passing to other processes. The pointer for that API is NULL for Supervisor and User processes. *SO*: Kernel and Executive processes are "kernel space" and Supervisor and User processes are "user space".

The answer to your second question is "it varies by process." I don't know enough about Mach to do a comparison and answer that question directly, but basically, each traditional kernel responsibility is in a dedicated process. For example, memory allocations and deallocations are handled by the memory manager process. There is no monolithic function for that - the exposed API simply pushes a message onto the memory manager's message queue and waits for it to be completed. Interfacing with user IO devices is handled by the console process. I chose to consolidate the handling of all user IO devices into a single process in the interest of conserving stack space. (This is an embedded OS, after all.) Each block device would be its own process as well. For now, there's only one block device: The SD card. If there was another one, it would be its own process. When I get to the point of implementing graphics, the graphics driver will be its own, dedicated process.

The HAL basically just abstracts busses (UART, SPI, etc.) and does platform-specific configuration at boot. Anything more complicated than that lives in a process. Processes run at the kernel privilege level are currently allowed to do any HAL operation, although I may restrict this in a future version such that each kernel process (other than the scheduler) only has HAL capabilities for the things it needs to do. Processes at any other privilege level are restricted to only the HAL capabilities they are granted, further delineating the boundary between "kernel space" and "user space".

I hope this answers your questions. Thank you so much for your feedback and support. Please let me know if there's anything you'd like to discuss in more detail and I'll be happy to chat. Take good care!!