-
POSEVEN_86: ideas of a plan
10 hours ago • 0 commentsTo make an approximate prototype of a POSEVEN system, simulation is possible but a bytecode generator and interpreter must be designed from scratch. It's possible and portable but slow and inherently limited in other ways.
So using the plain x86 (32 and 64-bit protected mode) as a platform is not so ridiculous:
- I still have good knowledge of the architecture and I'm not afraid to code in assembly language until a compiler is adapted,
- the CISC demon has a complex, but rich set of features, including page tables, so I can kneecap them at will,
- the code will execute at "native speed",
- NASM is the best x86 assembler and disassembler, and (still) under active development, and I am in contact with one of the maintainers, so it will be easy to "thoroughly adapt" ;-)
- NASM has a great macro system so it could emulate a RISC instruction set,
- there are many IO options to make pretty demos, keyboard video and mouse are pretty done,
- people can test-drive with a boot media or in a VM
Of course, the core promises of safety cannot be held, because the x86 architecture is too... hmmmm... but that lets me verify that my ideas and principles hold water for an actual functioning interactive system. Porting to a dedicated platform will be a breeze, hopefully.
The plan so far:
- Write pseudo-assembly code using the powerful macros of NASM, so the instructions look like RISC but they are converted to x86-64 under the hood. Since it's a macro-language, this would be a Macro Instruction Set (.mis extension)
- In the module build script, call the disassembler to parse for any access to segments, stack pointer, computed jumps, stack pointer... or invokes forbidden instructions.
- Get a computer that can boot from SD card or FreeDOS, and/or a virtual machine to boot the system.
-
The end of the linker
a day ago • 0 commentsOne unavoidable part of building a UNIX-like system, after the assembler and the compiler, is the linker. This is the piece that sticks all the parts of a program together, the system libraries, the program, the extra libraries, puts them in memory then patches the various areas such that code and data pointers have the right values. Salt and pepper with ASLR... The dynamic linker is an even tougher beast and several attacks have used this pathway.
"Modules" in POSEVEN are static. The compiler can optimise the hell out of a module and not care about layout of code and private data.
The only thing that needs load-time alteration is the instructions that call other modules since the numbers of the services are usually fixed, but the system may re-attribute them (substitution for debug, update, versioning etc.)
- In a pure RISC system, the loader can simply scan the binary code and detect the IPC instructions to directly "patch" the immediate numbers with local system values. Note: the service/module number must be immediate (16 bits usually) and the function number (index in the trampoline) is usually a register operand, which should not vary.
- On x86 PoC, the IPC instruction is emulated by a short sequence of instructions (macro), an address table maybe be present (alongside the code segment) to tell where to patch the immediate values, more shennanigans are possible as well, more or less dynamic.
But aside from this...
- There is no ASLR so no need to fix the code and data addresses. Most are immediate and absolute, to save on address calculations.
- There is no need to assemble disparate code or data segments together, no need to resolve labels, no name collisions...
Bye bye linker, the code to load another module has become incredibly small, because the cost of IPC (calling another module) has been reduced to almost nothing (in the intended platform, and x86 will of course be slow at the edges with the TLB things but at this point it does not matter).
-
FRUTE and Scheme
a day ago • 0 commentshttps://frute.net/ proposes a LISP machine-like environment.
There are interesting ideas but
- POSEVEN does not mandate a given programming language
- Tagged memory is an industrial dead end.
POSEVEN does not tag the memory, but implicitly the pointers, since the MSB already segregate the functions/access types. The applications are responsible for correctly handling the values and types. It's a reasonable compromise and structures such as #aStrA : Aligned Strings format with attributes do a reasonable job at embedding information in the pointers.
Furthermore, the data space can not contain pointer to code and no instruction is allowed to access (read, write or execute) the stack or the code areas. It is not possible to "leak" info in any direction. And since the data stack and control stack are split, the major "type confusion" attack (on the mixed stack) is prevented.
Tagging each memory location increases memory word widths (or reduces useable space) and requires either a dedicated circuit, or code to check the type. This is not a reasonable requirement and experience with early (non-optimising) Ada compilers is a lesson from 4 decades ago.
Asianometry has covered (superficially) LISP machines and I'm sure Duane will add more context and depth. But POSEVEN can not rely on word-granularity types. However, pages could be a good place to enforce this.
LISP machines have a certain appeal, just like Forth machines. But their flexibility is reduced to a certain class of applications, not those that POSEVEN targets.
-
POSEVEN on x86
a day ago • 0 commentsLooking at Linux, one would think that POSEVEN would be hard to adapt to the x86 / x86-64 platform.
But most of the basic infrastructure is already there ! it just has been mooted by the flat-layout UNIX kernels.
The trampolines are not there, but can still be emulated with a trampoline trampoline ;-)
But the 3 main memory spaces (+IO) are already part of the x86 architecture with the segments.
"Segments are slow!" but they serve important purposes that have been sacrificed on the alter of raw performance. Today, the prefixes make almost no difference when x86 CPUs run at 2, 3 even 4GHz !
- DS, ES : data segments.
- CS : code segment.
- SS : Stack segment
- IN and OUT instructions already exist
It's all here and implicit. Linux (and xBSD) just make them point to the same flat memory area.
FS and GS are extra features that are not even required.
So a quasi-POSEVEN kernel is possible on x86. Not that I want to get into this horrible mess again but I could at least NASM my way out of it easily.
Of course, there would be strong limitations:
- At least for most modules, segment prefixes are not allowed, as they would be the prime method to circumvent protection. The kernel would be allowed, though.
- Playing with SP would be forbidden. Only PUSH/POP (POPA or PUSHA would be OK)
- Only immediate calls and jumps/branches are allowed, so no computed jumps.
These could be enforced by modification of the assembler. But given the hardware's nature, it can only "emulate" POSEVEN (and quite fast, at that) but can't ensure all the safety guaranties. So that would be a good way to prototype and demonstrate the principles and get the first system in working condition without having to write a JVM or something like that.
It's feasible in a VM, at least, or with a bootstrap from FreeDOS.
The risk would be to get carried away with all the x86-specific "features" and taint the pure RISC philosophy.
-
Ideas to choose from Plan9
10/19/2026 at 17:48 • 0 commentsAfter several years of work on one project, when it hits too many roadblocks, I often have this tradition: See everything that worked and what didn't. I keep what worked and do the reverse of what hadn't worked.
I usually do this for my CPUs but here, let's talk about operating systems. POSEVEN is not an OS nor my second, but we can look at others and see what ultimately failed. I've ranted a LOT about UNIX because it's the main type in circulation (if you ignore MS Windows, but why wouldn't you ?)Now let's have a look at Plan 9.
![]()
This post is inspired by a post by Arndt Mueller :
Plan 9 from Bell Labs is a distributed research operating system created in the mid-1980s by the original creators of Unix (including Ken Thompson, Rob Pike, and Dennis Ritchie) as a planned successor to Unix.
Key Concepts
• Everything is a file: Building on Unix, Plan 9 extends the concept so that every resource, service, and device is represented as a file and accessed through a standard file tree.
• The 9P Protocol (Styx): A lightweight network file protocol that allows local and remote file systems, hardware devices, and processes to be shared seamlessly across a network.
• Per-process Namespaces: Each program can have its own customized, private view of the file system, a concept that later influenced modern Linux containers.
• Native UTF-8: Plan 9 was designed and built to handle UTF-8 Unicode text throughout the entire system, shell, and text editors (like sam and acme).
• Distributed Architecture: It was built to run across a network of specialized machines (separate CPU servers, terminal workstations, and storage servers like Venti) rather than a single monolithic computer.
![]()
I must admit that I have a good amount of respect for Plan9, as I was (remotely) introduced to it around 1999-2000 at uni by my OS/CS prof Jean Méhat (RIP). It was to be the future... But still in raw form. Experimental, barebones and no practical use for it, yet, only distributed in binary blobs with an interface that was... terse.
- Soon I would use the graphical interface port "9wm" on XWindow, regularly, a lot. It's small. It's simple. I even wrote an article about it. It's not the best but it's no fuss. I love simple things since they let me focus on the real work.
So minimalist GUI : ok.
- But I didn't use Plan9 for real. I could catch a few "party tricks" here and there as they filtered through Linux for example. That was... OK.
Some applications are nice. Others are cumbersome. Since UNIX/POSIX loves treating/processing text streams, this creates a new problem: when you have complex resources, you must create complex parsers and serialisers. It becomes more work for both the driver and the userland program. And more attack surface.
So I consider a dual track: some resources are "UNIX files" which is great for unstructured streams like regular files as well as /dev/random and such.
But structured resources? They must be properly handled like the "objects" they are. I don't like objects but this paradigm has certain fundamental merits when well used. And the "modules" of POSEVEN are the right interface, even though they are binary. You can still make a binary<->text converter (an idea for another standard module... sounds a bit like regedit???)
- The 9P Protocol (Styx):... A pretty great achievement. I like the idea though I don't know much more about it. And on POSEVEN nothing prevents you from transfering the IPC to a different machine using a buffer/translator/trampoline/surrogate module.
- Per-process Namespaces: This translates to POSEVEN with each running thread having (inheriting and then updating) their own directory of available and "translated" modules. Alteration of the list and mapping to actual modules can be on-demand (for example selecting a particular revision of the module/service) or external (for example for debug, inspection, debugging or the module needing a translation layer, whatever). Changing the module "on the fly" though is a more difficult challenge, particularly if the interface has not been closed/reset.
- Native UTF-8: This alone was maybe half of Plan9 project's genius. I have extended this to aStrA, a more complete, safer, maybe faster method that can keep baseline ASCII from being polluted by UTF-8, reducing the attack surface and uncertainty.
- Distributed Architecture: POSEVEN does not mandate a physical architecture, at least not at that level. Plan9 was born in the single-core era but today we usually run dozens of threads simultaneously on separate cores, so a modules-based programming model is "distributed" by default. Your thread could ask to run close to another thread, but that's implementation-dependent. Modules are not inherently tied to a physical resource, and your RAM, your CPU, can be virtualised at will by the other modules you call.
Let's add, from Wikipedia:
- Plan 9 also introduced capability-based security and a log-structured file system called Fossil that provides snapshotting and versioned file histories.
Yes, please.
Conclusion:
Plan9 was not the hit it should/could have been but its legacy is indisputable. There are tons of ideas to pick and enhance, adjust and repackage, the Linux crowd didn't hesitate. I think Inferno and others took off from there. This also shows that the UNIX creators knew that UNIX was a dead end.Please share your opinions in the comments sections :-)
- Soon I would use the graphical interface port "9wm" on XWindow, regularly, a lot. It's small. It's simple. I even wrote an article about it. It's not the best but it's no fuss. I love simple things since they let me focus on the real work.
-
Interrupts
09/12/2026 at 17:16 • 0 commentsHandling interrupts is very easy : these signals are treated like a IPC instruction.
Each class of interrupt (such as maskable, non-maskable, system, IO, SW faults...) has a dedicated register with the n° of the thread (and its thread descriptor address) that manages it (plus a "ready" flag). Vectored interrupts in the class are managed through the IPC service n°. Thus there is very little special hardware to add, and no new instruction.
A handling thread must be spawned ("pre-fork" in other contexts) before the interrupt source can be serviced.
There is no other requirement (yet), there may be as many classes as necessary and possible.
The interrupt registers are not specified yet.
-
Auxiliary Input and Output space
02/19/2026 at 22:03 • 0 commentsInput and output (IO) is another critical resource but it's not the central feature of POSEVEN. It still needs to be specified !
The Code, Data and Instruction spaces are essential but some modules may inherit access to a 4th space : IO registers.
Access to this space is exclusive : each module (why limit to thread?) can access a range (only one range ?) of IO registers, which are rather fast, but must usually enforce access protocols.
When a thread generates an input or output request, the IO target/peripheral compares the module ID or thread ID with a valid registered ID (configured by the core module). An ID mismatch will obviously trigger an exception.
The single-server approach might look like it increases complexity and latency, becoming a bottleneck, but overall, it ensures a coherent multiplexing of the requests as the system scales up, preventing complex semaphores, mutual locks and race conditions.
A particular IO module may receive simultaneous requests through IPC/IPE from several threads, but this leads to one single code, a single point of decision, simplifying arbitrage. There is no risk that one module mismanages a resource shared with another module using an outdated protocol, for example.
Furthermore, large data blocks may be transmitted through the "shuttles" and queued during the IPC, and the main thread of the module will handle and serialise all the requests.
-
The trampoline
02/08/2026 at 23:42 • 0 commentsA major feature of the POSEVEN specification is the trampoline zone at the start of the code area.
One of the aspects is the protocol that preserves speed and security. Hardware protection makes it impossible to access accidentally but the module itself must participate with its own filtering code.
The trampoline spans the lowest 64Ki-instruction page of the code area. Granularity would be 4 or 8 instructions so only 8Ki to 16Ki entry points (maximum) are possible.
The trampoline zone is accessed directly from a different module by a special JUMP instruction called IPC, which expects to point to an IPE instruction. This ensure that random jumps or even scanning (spraying) do not work.
Returning to the calling module goes through the IPR instruction, which must re-execute the calling IPC instruction to ensure the stack's integrity (and restore some registers) (so IPC is a two-mode instruction).
IPE must be located at the first instruction of a cache line (otherwise: trap), for safety and speed. The rest of the cache line (and the following line(s)) may filter the parameter, and trap or return immediately in case of error, otherwise the code jumps to the actual code, outside of the small (somewhat) zone.
IPR can be executed anywhere.
-----------------------------------
Since IPE can only exist at certain addresses, the OS loader (that configures the program) can simply scan the first 64K instructions of a program, looking for the IPE opcode, and re-create a table of valid entry points. This makes it easy to create new versions of a module that preserves the high-level entry points but implements it differently (as long as the entry points remain in the same order).
The calling programs are distributed with binaries that IPC to an entry point number, and the loader must "relocate", translate that number to the actual address of the instruction. This lookup at load time removes expensive/slow runtime lookups.
So this whole system ensures that a dynamic library, the kernel, an application, a device driver.... use the same and only one file format and structures, there is no special case to manage in the processor.
The trampoline also defines the granularity of the pages for code : 64K instructions per page. This first page is always (or preferably) resident, in order to keep the system fast. And with 64Ki instructions per page, a module has a low maximum number of pages. Depending on the maximum number of bits of the Program Counter:
- 20 bits : 16 pages
- 22 bits : 64 pages
- 24 bits : 256 pages
This lets the iTLB hold more simultaneous modules in cache.
----------------------------
Now what can the trampoline be compared to ?
- for Unix, that would be _start and all the signal()s. Signals are asynchronous, so is POSEVEN, so there is an expectation of concurrency. Except that absolutely arbitrary signals can be sent to a module, any quantity at any rate with any parameter....
- The trampoline is also inspired by the interrupt/trap structure of certain DSP/CPUs, so an IRQ can directly trigger a given codepath in a given module.
- For an object-oriented programming language, the trampoline correspond to the declaration of all public functions.
- For Pascal, the trampoline is more or less the declarative part of a "unit". In fact, in POSEVEN, every module is a "unit", and the init system (ID0) calls all the other units.
So the trampoline makes all the entry points safely accessible, allowing filtering of the requests. It makes the OOP paradigm more visible at the OS/System level. It maps directly to many common programming paradigms.
------------------------------
Remember :
By default, any module may call any other module at any entry point. Spraying is not impossible and there is always the risk that "rogue discovery" takes place.
Each module is responsible for filtering the callee : IPE provides the caller's ID and it must be compared (as well as capabilities) with safe callers. Some HW features must help expedite this frequent check.
- The callee may trap and / or signal the OS of a rogue call.
- Or if it provides a system-wide feature, there is no filtering.
--------------------------
Note : since the trampoline starts at address #0 then ASLR is not possible. But POSEVEN makes it useless anyway, for many reasons (split address spaces, blackbox instruction space, impossibility to jump to arbitrary addresses...)
-
Memory model
12/23/2024 at 02:02 • 2 commentsA big part of the programming model is the memory model : the organisation of data in memory, their roles and restrictions...
POSEVEN follows the CDI (Control/Data/Instructions) model and has three memory spaces (four if the IO ports are counted) that are independent from each other : a pointer for one space has no meaning in another space. All spaces can be paged out and cached, but the independence prevents one space from thrashing another's.1) Stack (the "Control" space)
The first and most simple memory space is the control stack. It is not accessed directly or indirectly by the program, hardware directly manages the addressing and the pointers have tags automatically added to them to prevent any confusion. Each level of stack contains two words : a data word and a pointer+tags word. Each level counts as "one" increment of the stack pointer, there is also a couple of "limit" indices to help catch abnormal conditions. This separate and protected stack is essential for the implementation of fast and efficient calls to other modules. There is no special constraint to the size of the index or the stack area, but it is usually limited by the word size : 32 or 64 bits, though going 1 billion deep (30 bits) would be pointless (see here)
The stack space is specific to the thread. Nothing else (except thread #0 and its surrogate) can access it, to preserve the integrity of the control flow. AFAIK, the only reasons to access it outside of the running thread is for setup/diagnosis/debugging.
2) Program
The second area is the "Instructions" space. All instructions are supposed to have the same size so incrementing the instruction pointer (PC) directly points to the next instruction (sorry : no gadget for your ROP). There is no data and the program can not read its own "data" from this space. This is a "black box" with no "introspection", meaning that no ROP (or other hacking) can occur. The code is immutable and only the source code can know what is where, apart from the IPE in the trampoline (see below).
The address size for a module is limited to 24 bits, probably even 22 bits (amounting to 16MB). Or 20 maybe (TBD by the ISA). With such a small size, relative addressing is pointless and the module can address itself with direct addresses embedded in the opcodes. Indirect or computed addressing is strictly limited. If you need more, modularise your program, don't let it bloat!
Each program space is specific to a module. An IPC instruction switches the current thread to a different module with its new program space. Pointers/instruction addresses for one module are irrelevant to a different module.
The program space is mostly homogeneous. The exception is the first 64K instructions which, at certain aligned addresses, can contain IPE instructions to allow jumping from a different module.
3) Data
Data belong to their own space. It is not homogeneous in its function, but the structure is always the same: a linear space made of 8-bit bytes, with a pointer that is as wide as words (32 or 64 bits).
The MSB of the address/pointer directs to one of four main areas: The top MSB is the private(0)/Public(1) flag. Other lower MSB divide this space even more, and the address generators can trap when a pointer calculation overflows or underflows, creating a pointer to a different sub-area.
00 : Data Stack, where parameters and recursive short-term blocs are stored.
01 : Local/Thread-private data (private heap, local/private data, variables)
10 : Module shared data, constants, holding the module's state, semaphores, configuration...
11 : Interchange area, used to allocate buffers that can be sent to other modules (message passing, "shuttles")
A pointer or address has a different meaning and shareability depending on the area/MSB:
- a private pointer (MSB=0) can't be shared at all and will have a totally different meaning in another thread.
- a module-shared area pointer (10) can be shared among threads running said module, but it's irrelevant in any other module.
- The interchange area (11) is the only type of pointer that can have meaning across all the threads and modules, BUT a given page can only be owned by ONE thread, which can then "yield" it to another recipient of its choice to transfer data (during IPC usually).
....
Overall, it's not simple but it's not overly complicated either. It does not require a complex support hardware/circuit/mechanism and it is reasonably efficient for 32-bit and 64-bit machines. Unlike a monokernel, it starts to make sense when the system implements tens or hundreds of threads and modules simultaneously, even in an embedded system.
......................
Update 20250304:
![]()
I have swapped the "high" and "low" parts so constants can be sign-extended easily for the most common/frequent use cases : accessing global module variables as well as thread private variables.
......................
Update 20260208:
Inverting the MSB of the data pointer feels pointless, since the NULL page could as easily be mapped to the bottom of the Data Stack. But then an additional TLB entry and associated memory is required per thread instead of per module. Except that the Null pointer TLB entry is never loaded in TLB, such that it triggers handling code.
Now it does not matter what address the data stack starts from, however it's good to have small offsets for the main data of a module, such that they can be accessed by a small immediate field.
To Be Discussed.
.
I also like how "privacy" decreases as the pointer's numerical value augments.
Also not shown is the different granularity of the pointers :
- Control/Stack pointers have a granularity of 2 words (2×32 or 2×64 bits)
- Data pointers point to individual 8-bit bytes
- Instruction pointers are undefined, usually 32-bit words with RISC but could be wider or narrower if needed.
.
-
Basics
07/11/2024 at 00:41 • 1 commentPOSEVEN is based on a platform (with a proper mix of hardware and software features) that provides/implements the following two fundamental elements :
1. Threads
A thread is a stream of execution : a sequence of instructions that are fetched, decoded and operated upon, one after the other, with the eventual and conditional change in the sequence that is controlled by the program and/or external data.
Nothing new here.
A POSEVEN system may implement an arbitrary number of threads, which may be limited by hardware constraints.
A thread can be started, paused, resumed, interrupted, frozen, thawed, swapped in/out or terminated at any time.
A POSEVEN thread has a "global" context that contains
- hidden, private and public properties
- capabilities, access rights, tokens...
- a protected stack space
- a private as well as a shared data address space.
A thread executes code from one module at a time.
2. Modules
A POSEVEN system hosts a number of modules: they contain an area of code to execute, associated with a common data memory area.
The code is a static collection of instructions to be executed by a thread. The thread can switch from one module to another by "calling" entry points in the desired module's trampoline area, located in the first instructions of the module.
The common data area is accessed only by the threads that execute the code of the associated module. Since any number of threads can execute a module simultaneously, all the code must be reentrant, and the relevant variables must be protected by semaphores, spinlocks or Mutexes.
.
Threat model
POSEVEN imposes modularity to promote code reuse and contain/isolate the flaws. The POSEVEN system assumes that any thread or module could be "rogue". It is the implementer's job to ensure that the "kernel" module (#0) and the "kernel" thread (ThId#0) provide the appropriate "root of trust".
Any module can call, or be called by, a module that is potentially altered (accidentally or intentionally) so a strict separation of the modules and threads is further required.
Communication between modules and/or threads uses strict safe interfaces:
- The trampoline area lets a module filter calls and decide which is legitimate. Each module enforces their own (potentially dynamic) policy, by reading the "capabilities" and other attributes of the calling modules & thread.
- Data is shared through a common data addressing area, where pointers are valid for pairs of threads. One thread may read and the other may write (but not both).
- Calling a module is often preceded by "shielding" the thread's data stack from reads & writes, and clearing all irrelevant registers to prevent any leaks. Extra data may be left over on top of the stack by a callee module, but it can't alter the data of the caller module.
- Each thread has its own protected stack that can only be accessed by itself, in order, through specific instructions, to prevent leaks, alterations, ROP (Return-Oriented Programming). Only the kernel and an authorised debugger can access another thread's stack's contents.
- Paged memory helps enforce some of the security policies and helps map, or remap, code and data areas. For example : page mapping allows a given thread to access the trampoline area of another module, which enables dynamical module substitution.
- "Resources" (hardware-controlled) may only be accessed by the "kernel" (thread #0) or one other thread, whose number is set by the kernel. In other words : the kernel manages which thread may access a given hardware resource at each time, and only one thread may handle a peripheral at a time, preventing any contention or race condition.
POSEVEN can not prevent hardware alteration or physical bypass of access rights/tokens. However it is designed to prevent, detect and isolate "unwanted" behaviour without getting too much in the way of an application, which is free to define its own policy. Adherence to security and safety rules is the responsibility of the system designer and implementation, but the system is considered unsafe unless proved otherwise.
Yann Guidon / YGDES

