mirror of
git://projects.qi-hardware.com/iris.git
synced 2025-04-21 12:27:27 +03:00
add license; reorganize arch
This commit is contained in:
@@ -1,3 +1,20 @@
|
||||
% Iris: micro-kernel for a capability-based operating system.
|
||||
% cross-compiler.tex: Cross-compiler building instructions.
|
||||
% Copyright 2009 Bas Wijnen <wijnen@debian.org>
|
||||
%
|
||||
% This program is free software: you can redistribute it and/or modify
|
||||
% it under the terms of the GNU General Public License as published by
|
||||
% the Free Software Foundation, either version 3 of the License, or
|
||||
% (at your option) any later version.
|
||||
%
|
||||
% This program is distributed in the hope that it will be useful,
|
||||
% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
% GNU General Public License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU General Public License
|
||||
% along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\documentclass{shevek}
|
||||
\begin{document}
|
||||
\title{Setting up a cross-compiler}
|
||||
|
||||
@@ -1,16 +1,33 @@
|
||||
% Iris: micro-kernel for a capability-based operating system.
|
||||
% kernel.tex: Description of Iris.
|
||||
% Copyright 2009 Bas Wijnen <wijnen@debian.org>
|
||||
%
|
||||
% This program is free software: you can redistribute it and/or modify
|
||||
% it under the terms of the GNU General Public License as published by
|
||||
% the Free Software Foundation, either version 3 of the License, or
|
||||
% (at your option) any later version.
|
||||
%
|
||||
% This program is distributed in the hope that it will be useful,
|
||||
% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
% GNU General Public License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU General Public License
|
||||
% along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\documentclass{shevek}
|
||||
\begin{document}
|
||||
\title{Overview of my kernel}
|
||||
\title{Overview of Iris}
|
||||
\author{Bas Wijnen}
|
||||
\date{\today}
|
||||
\maketitle
|
||||
\begin{abstract}
|
||||
This document briefly describes the inner workings of my kernel, including the
|
||||
reasons for the choices that were made. It is meant to be understandable (with
|
||||
effort) for people who know nothing of operating systems. On the other hand,
|
||||
it should also be readable for people who know about computer architecture, but
|
||||
want to know about this kernel. It is probably better suited for the latter
|
||||
category.
|
||||
This document briefly describes the inner workings of my kernel, Iris,
|
||||
including the reasons for the choices that were made. It is meant to be
|
||||
understandable (with effort) for people who know nothing of operating systems.
|
||||
On the other hand, it should also be readable for people who know about
|
||||
computer architecture, but want to know about this kernel. It is probably
|
||||
better suited for the latter category.
|
||||
\end{abstract}
|
||||
|
||||
\tableofcontents
|
||||
@@ -20,7 +37,7 @@ This section describes what the purpose of an operating system is, and defines
|
||||
what I call an ``operating system''\footnote{Different people use very
|
||||
different definitions, so this is not as trivial as it sounds.}. It also goes
|
||||
into some detail about microkernels and capabilities. If you already know, you
|
||||
can safely skip this section. It contains no information about my kernel.
|
||||
can safely skip this section. It contains no information about Iris.
|
||||
|
||||
\subsection{The goal of an operating system}
|
||||
In the 1980s, a computer could only run one program at a time. When the
|
||||
@@ -160,8 +177,8 @@ into the hands of the user (as far as allowed by the system administrator).
|
||||
This is a very good thing.
|
||||
|
||||
\section{Kernel objects}
|
||||
This section describes all the kernel objects, and the operations that can be
|
||||
performed on them. One operation is possible on any kernel object (except a
|
||||
This section describes all kernel objects of Iris, and the operations that can
|
||||
be performed on them. One operation is possible on any kernel object (except a
|
||||
message and reply and call Capabilities). This operation is \textit{degrade}.
|
||||
It creates a copy of the capability with some rights removed. This can be
|
||||
useful when giving away a capability.
|
||||
@@ -191,13 +208,13 @@ Receiver's queues.
|
||||
\item Get and set the limit, which is checked when allocating pages for this
|
||||
Memory or any sub-structure.
|
||||
\item Drop a capability. This can only be done by Threads owned by the Memory,
|
||||
because only they can present capabilities owned by it.\footnote{The kernel
|
||||
checks if presented capabilities are owned by the Thread's Memory. If they
|
||||
aren't, no capability is passed instead. The destroy operation destroys an
|
||||
object that a capability points to. Drop destroys the capability itself. If a
|
||||
Thread from an other Memory would try to drop a capability, the kernel would
|
||||
refuse to send it in the message, or it would not be dropped because it would
|
||||
be owned by a different Memory.}
|
||||
because only they can present capabilities owned by it.\footnote{Iris checks if
|
||||
presented capabilities are owned by the Thread's Memory. If they aren't, no
|
||||
capability is passed instead. The destroy operation destroys an object that a
|
||||
capability points to. Drop destroys the capability itself. If a Thread from
|
||||
an other Memory would try to drop a capability, Iris would refuse to send it in
|
||||
the message, or it would not be dropped because it would be owned by a
|
||||
different Memory.}
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Receiver}
|
||||
@@ -249,15 +266,15 @@ flags (whether the process is running or waiting for a message, setting these
|
||||
flags is a way to control this for other Threads), the program counter and the
|
||||
stack pointer. This call is also used to get the contents of processor
|
||||
registers and possibly other information which is different per Thread.
|
||||
\item Let the kernel schedule the next process. This is not thread-specific.
|
||||
\item Let Iris schedule the next process. This is not thread-specific.
|
||||
\item Get the top Memory object. This is not thread-specific. Most Threads
|
||||
are not allowed to perform this operation. It is given to the initial Threads.
|
||||
They can pass it on to Threads that need it (mostly device drivers).
|
||||
\item In the same category, register a Receiver for an interrupt. Upon
|
||||
registration, the interrupt is enabled. When the interrupt arrives, the
|
||||
registered Receiver gets a message from the kernel and the interrupt is
|
||||
disabled again. After the Thread has handled the interrupt, it must reregister
|
||||
it in order to enable it again.
|
||||
registered Receiver gets a message from Iris and the interrupt is disabled
|
||||
again. After the Thread has handled the interrupt, it must reregister it in
|
||||
order to enable it again.
|
||||
\item And similarly, allow these priviledged operations (or some of them) in an
|
||||
other thread. This is a property of the caller, because the target thread
|
||||
normally doesn't have the permission to do this (otherwise the call would not
|
||||
@@ -286,8 +303,8 @@ all flags can be set in all cases.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Capability}
|
||||
A capability object can be invoked to send a message to a receiver or the
|
||||
kernel. The owner cannot see from the capability where it points. This is
|
||||
A capability object can be invoked to send a message to a receiver or to Iris
|
||||
itself. The owner cannot see from the capability where it points. This is
|
||||
important, because the user must be able to substitute the capability for a
|
||||
different one, without the program noticing. In some cases, it is needed to
|
||||
say things about capabilities. For example, a Memory can list the Capabilities
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
% Iris: micro-kernel for a capability-based operating system.
|
||||
% making-of.tex: Description of the process of writing Iris.
|
||||
% Copyright 2009 Bas Wijnen <wijnen@debian.org>
|
||||
%
|
||||
% This program is free software: you can redistribute it and/or modify
|
||||
% it under the terms of the GNU General Public License as published by
|
||||
% the Free Software Foundation, either version 3 of the License, or
|
||||
% (at your option) any later version.
|
||||
%
|
||||
% This program is distributed in the hope that it will be useful,
|
||||
% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
% GNU General Public License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU General Public License
|
||||
% along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
\documentclass{shevek}
|
||||
\begin{document}
|
||||
\title{Writing a kernel from scratch}
|
||||
@@ -5,13 +21,13 @@
|
||||
\date{\today}
|
||||
\maketitle
|
||||
\begin{abstract}
|
||||
This is a report of the process of writing a kernel from scratch for
|
||||
This is a report of the process of writing a kernel (Iris) from scratch for
|
||||
the cheap (€150) Trendtac laptop. In a following report I shall write about
|
||||
the operating system on top of it. It is written while writing the system, so
|
||||
that no steps are forgotten. Choices are explained and problems (and their
|
||||
solutions) are shown. After reading this, you should have a thorough
|
||||
understanding of the kernel, and (with significant effort) be able to write a
|
||||
similar kernel yourself. This document assumes a working Debian system with
|
||||
the operating system on top of it. This document is written while writing the
|
||||
system, so that no steps are forgotten. Choices are explained and problems
|
||||
(and their solutions) are shown. After reading this, you should have a
|
||||
thorough understanding of Iris, and (with significant effort) be able to write
|
||||
a similar kernel yourself. This document assumes a working Debian system with
|
||||
root access (for installing packages), and some knowledge about computer
|
||||
architectures. (If you lack that knowledge, you can try to read it anyway and
|
||||
check other sources when you see something new.)
|
||||
@@ -67,7 +83,9 @@ mapped (meaning it accesses physical, not virtual, memory)
|
||||
except that is is not cached.
|
||||
\item kseg2 runs from 0xc0000000 to the top. It is mapped like user memory,
|
||||
differently for each process, and can be cached. It is intended for
|
||||
per-address space kernel structures. I shall not use it in my kernel.
|
||||
per-address space kernel structures. I shall not use it in Iris.\footnote{I
|
||||
thought I wouldn't use kseg2. However, I needed to use it for kernel entry
|
||||
code, as you can read below.}
|
||||
\end{itemize}
|
||||
U-boot has some standard commands. It can load the image from the SD card at
|
||||
0x80600000. Even though the Linux image seems to use a different address, I'll
|
||||
@@ -181,7 +199,7 @@ It explains that memory accesses to the lower 2GB are (almost always) mapped
|
||||
through a TLB (translation lookaside buffer). This is an array of some records
|
||||
where virtual to physical address mappings are stored. In case of a TLB-miss
|
||||
(the virtual address cannot be found in the table), an exception is generated
|
||||
and the kernel must insert the mapping into the TLB.
|
||||
and Iris must insert the mapping into the TLB.
|
||||
|
||||
This is very flexible, because I get to decide how I write the kernel. I shall
|
||||
use something similar to the hardware implementation of the IBM PC: a page
|
||||
@@ -197,16 +215,16 @@ what is more expensive. I'll initially go for the cpu time wasting approach.
|
||||
|
||||
\section{Kernel entry}
|
||||
Now that I have an idea of how a process looks in memory, I need to implement
|
||||
kernel entry and exit. A process is preempted or makes a request, then the
|
||||
kernel responds, and then a process (possibly the same) is started again.
|
||||
kernel entry and exit. A process is preempted or makes a request, then Iris
|
||||
responds, and then a process (possibly the same) is started again.
|
||||
|
||||
The main problem of kernel entry is to save all registers in the kernel
|
||||
structure which is associated with the thread. In case of the MIPS processor,
|
||||
there is a simple solution: there are two registers, k0 and k1, which cannot be
|
||||
used by the thread. So they can be set before starting the thread, and will
|
||||
still have their values when the kernel is entered again. By pointing one of
|
||||
them to the place to save the data, it becomes easy to perform the save and
|
||||
restore.
|
||||
still have their values when the kernel is entered again.\footnote{This is not
|
||||
true, see below.} By pointing one of them to the place to save the data, it
|
||||
becomes easy to perform the save and restore.
|
||||
|
||||
As with the bootstrap process, this must be done in assembly. In this case
|
||||
this is because the user stack must not be used, and a C function will use the
|
||||
@@ -221,12 +239,12 @@ easiest way.
|
||||
Now I've reached the point where I need to create some memory structures. To
|
||||
do that, I first need to decide how to organize the memory. There's one very
|
||||
simple rule in my system: everyone must pay for what they use. For memory,
|
||||
this means that a process brings its own memory where the kernel can write
|
||||
things about it. The kernel does not need its own allocation system, because
|
||||
it always works for some process. If the process doesn't provide the memory,
|
||||
the operation will fail.\footnote{There are some functions with \textit{alloc}
|
||||
in their name. However, they allocate pieces of memory which is owned by the
|
||||
calling process. The kernel never allocates anything for itself, except during
|
||||
this means that a process brings its own memory where Iris can write things
|
||||
about it. Iris does not need her own allocation system, because she always
|
||||
works for some process. If the process doesn't provide the memory, the
|
||||
operation will fail.\footnote{There are some functions with \textit{alloc} in
|
||||
their name. However, they allocate pieces of memory which is owned by the
|
||||
calling process. Iris never allocates anything for herself, except during
|
||||
boot.}
|
||||
|
||||
Memory will be organized hierarchically. It belongs to a container, which I
|
||||
@@ -235,11 +253,11 @@ Memory, its parent. This is true for all but one, which is the top level
|
||||
Memory. The top level Memory owns all memory in the system. Some of it
|
||||
directly, most of it through other Memories.
|
||||
|
||||
The kernel will have a list of unclaimed pages. For optimization, it actually
|
||||
has two lists: one with pages containing only zeroes, one with pages containing
|
||||
junk. When idle, the junk pages can be filled with zeroes.
|
||||
Iris will have a list of unclaimed pages. For optimization, she actually
|
||||
has two lists: one with pages containing only zeroes, and one with pages
|
||||
containing junk. When idle, the junk pages can be filled with zeroes.
|
||||
|
||||
Because the kernel starts at address 0, building up the list of pages is very
|
||||
Because Iris starts at address 0, building up the list of pages is very
|
||||
easy: starting from the first page above the top of the kernel, everything is
|
||||
free space. Initially, all pages are added to the junk list.
|
||||
|
||||
@@ -251,7 +269,7 @@ task, and then jumping to it.
|
||||
|
||||
There are two options for the idle task, again with their own drawbacks. The
|
||||
idle task can run in kernel mode. This is easy, it doesn't need any paging
|
||||
machinery then. However, this means that the kernel must read-modify-write the
|
||||
machinery then. However, this means that Iris must read-modify-write the
|
||||
Status register of coprocessor 0, which contains the operating mode, on every
|
||||
context switch. That's quite an expensive operation for such a critical path.
|
||||
|
||||
@@ -286,16 +304,17 @@ first switch off all interrupts by writing 0 to the Status register of CP0.
|
||||
This also reminded me that I need to flush the cache, so that I can be sure
|
||||
everything is correct. For that reason, I need to start at 0xa0000000, not
|
||||
0x80000000, so that the startup code is not cached. It should be fine to load
|
||||
the kernel at 0x80000000, but jump in at the non-cached location anyway, if I
|
||||
Iris at 0x80000000, but jump in at the non-cached location anyway, if I
|
||||
make sure the initial code, which clears the cache, can handle it. After that,
|
||||
I jump to the cached region, and everything should be fine. However, at this
|
||||
moment I first link the kernel at the non-cached address, so I don't need to
|
||||
worry about it.
|
||||
moment I first link Iris at the non-cached address, so I don't need to
|
||||
worry about it.\footnote{Actually, it seems that the cache is working fine, and
|
||||
I'm using the cached address. They are used for kernel entry in any case.}
|
||||
|
||||
Finally, I read in the books that k0 and k1 are in fact normal general purpose
|
||||
registers. So while they are by convention used for kernel purposes, and
|
||||
compilers will likely not touch them, the kernel can't actually rely on them
|
||||
not being changed by user code. So I'll need to use a different approach for
|
||||
compilers will likely not touch them, Iris can't actually rely on them not
|
||||
being changed by user code. So I'll need to use a different approach for
|
||||
saving the processor state. The solution is trivial: use k1 as before, but
|
||||
first load it from a fixed memory location. To be able to store k1 itself, a
|
||||
page must be mapped in kseg3 (wired into the tlb), which can then be accessed
|
||||
@@ -350,15 +369,15 @@ So now I need to accept calls from programs and handle them. For this, I need
|
||||
to decide what such a call looks like. It will need to send a capability to
|
||||
invoke, and a number of capabilities and numbers as arguments. I chose to send
|
||||
four capabilities (so five in total) and also four numbers. The way to send
|
||||
these is by setting registers before making a system call. Similarly, when the
|
||||
kernel returns a message, it sets the registers before returing to the program.
|
||||
these is by setting registers before making a system call. Similarly, when
|
||||
Iris returns a message, she sets the registers before returing to the program.
|
||||
|
||||
I wrote one file with assembly for receiving interrupts and exceptions
|
||||
(including system calls) and one file with functions called from this assembly
|
||||
to do most of the work. For syscall, I call an arch-specific\footnote{I split
|
||||
off all arch-specific parts into a limited number of files. While I am
|
||||
currently writing the kernel only for the Trendtac, I'm trying to make it easy
|
||||
to port it to other machines later.} invoke function, which reads the message,
|
||||
currently writing Iris only for the Trendtac, I'm trying to make it easy to
|
||||
port her to other machines later.} invoke function, which reads the message,
|
||||
puts it in variables, and calls the real invoke function.
|
||||
|
||||
The real invoke function analyzes the called capability: if it is in page 0
|
||||
@@ -449,9 +468,9 @@ but can be reused.
|
||||
Another nice optimization is \textit{copy on write}: a page is shared
|
||||
read-only, and when a page-fault happens, the kernel will copy the contents, so
|
||||
that the other owner(s) don't see the changes. For the moment, I don't
|
||||
implemnt this. I'm not sure if I want it in the kernel at all. It can well be
|
||||
implemented using an exception handler in user space, and is not used enough to
|
||||
spend kernel space on, I think. But I can change my mind on that later.
|
||||
implemnt this. I'm not sure if I want it in Iris at all. It can well be
|
||||
implemented using an exception handler in user space, and may not be used
|
||||
enough to spend kernel space on. But I can change my mind on that later.
|
||||
|
||||
\section{Memory listing}
|
||||
The last thing to do for now is allowing a memory to be listed. That is,
|
||||
@@ -459,4 +478,14 @@ having a suitably priviledged capability to a Memory should allow a program to
|
||||
see what's in it. In particular, what objects it holds, and where pages are
|
||||
mapped. Probably also what messages are in a receiver's queue.
|
||||
|
||||
\section{A name for the kernel}
|
||||
However, at this point I am publishing the existence of the kernel, and so I
|
||||
need to give it a name. I like Greek mythology, so I decided to make it a
|
||||
Greek god. Because the kernel is mostly doing communication between programs,
|
||||
while the programs do the real work on the system, I thought of Hermes, the
|
||||
messenger of the gods. However, I don't really like his name, and I want a
|
||||
logo which is furrier than a winged boot or staff. So I chose Iris, who is
|
||||
also a messenger of gods, but she has a rainbow symbol. This is much nicer for
|
||||
creating a logo.
|
||||
|
||||
\end{document}
|
||||
|
||||
Reference in New Issue
Block a user