No longer maintained. I made some wrong design choices and it's now almost impossible to fix them without rewriting a lot of code. Feel free to fork and experiment on your own.
Cocos is an experimental microkernel operating system written in C, targeting the RISC-V 64-bit architecture. It is designed for educational purposes and to explore microkernel concepts such as IPC, user-space servers, and capability-based resource management.
- Microkernel Architecture: Minimal kernel handling IPC, scheduling, and basic memory management (a bit hybrid).
- No Permission Mechanism: The kernel does not enforce permission checks. Cocos is just an experimental OS for learning, and we trust all user-space components.
- IPC: Synchronous message passing and asynchronous notifications.
- User-space Servers:
pm: Process Manager.vsfs: Very Simple File System.ramdisk: RAM disk driver.uart16550: Serial driver.idle: Idle task.random: Random number generator.echo: Simple echo server.
- User-space Applications: Shell, standard utilities (
ls,mount,mkdir, etc.). - Platform: We implement an HAL, but now only target QEMU RISC-V Virt.
kernel/: The core microkernel source code.arch/: Architecture-specific code.ipc/: Inter-Process Communication implementation.mm/: Memory management.syscall/: System call handlers.task/: Task scheduling and management.
uspace/: User-space components.apps/: User applications (shell, utils).libc/: C library implementation.servers/: System servers (pm, fs, drivers).
libs/: libcocos - Shared kernel/user libraries.include/: Header files.docs/: Design documentation.scripts/: Build and utility scripts.
To build and run Cocos, you need the following tools:
- RISC-V Toolchain:
riscv64-unknown-elf-gcc,ld, etc. - QEMU:
qemu-system-riscv64for emulation. - Make: Build system.
- Python 3: For utility scripts.
-
Build the project:
make -j$(nproc)This will build the kernel, user-space libraries, servers, and applications.
You could specify some kernel configurations in
Config.mkbefore building, or pass them as arguments tomake, e.g.make KLOG=1 ULOG=1 LLOG=1. -
Run in QEMU:
make run
This will start the OS in QEMU. You should see the shell prompt.
make run-logis also available to enable serial logging to a filebuild/qemu.log, which is useful for debugging. -
Debug:
- Start GDB server:
make gdb-server
- Connect with GDB client (in another terminal):
make gdb-client
- Start GDB server:
For convenience and scalability, we introduce a custom build system based on Makefiles - CBuild (just like Linux's KBuild), whose name stands for Cocos Build System. You can find its core implementation in scripts/build.mk.
The usage of CBuild is similar to KBuild, but with tons of simplifications. Let's take adding a new server as an example:
- Create a new folder under
uspace/servers/, e.g.myserver/. - You write some source files in it, e.g.
myserver.candlibmyserver.c. - Create a
CBuildfile right undermyserver/with the following content:obj-y := myserver.o libmyserver.o - Finally, add the new server to the list of servers in top-level Makefile thus the server will be built and linked into the final image:
And that's it! You have successfully added a new server to Cocos.
export SERVERS := \ ... myserver \ ...
Some configuration can also be done easily. For example, we can add a new optional server fat32 to cocos. To do this, just replace the obj-y := ... line in uspace/servers/fat32/CBuild with:
obj-$(FAT32_SUPPORT) := ...Then, in Config.mk, we can enable or disable this server by setting:
FAT32_SUPPORT := y # or nOr you can directly pass the configuration when invoking make:
make FAT32_SUPPORT=y ...Sometime your server or application may depend on some custom dependencies. such as for ramfs server, it may depend on a initrd.img file to be included in the final image. You can achieve this by fake a custom target in the CBuild file:
obj-y := ramfs.o libramfs.o initrd.o
initrd.o: $(TOPDIR)/path/to/initrd.img
@cp $< $@Cocos follows a microkernel design where the kernel provides minimal mechanisms, and policies are implemented in user-space servers.
- Direct IPC: The primary mechanism for communication between tasks and servers.
- Process Management: The
pmserver manages process creation and lifecycle. - Drivers: Drivers like
uart16550andramdiskrun in user space. - Name Space: Each process can have its own namespace managed by the
pmserver, thus allowing isolation and different views of the whole system.pmserver provides a basic service to publish/unpublish and lookup services by name. And processes can mount/umount services they need to their namespace.(e.g. mounting servicedisk0to/dev/disk0, mounting servicefs0to/). - File System: The
vsfsserver implements the file system, communicating with drivers likeramdisk.
More detailed design documents can be found in the docs/ directory (Note: some documents might be work-in-progress or reflect earlier designs).