Update markdown and tabs and stuff
This commit is contained in:
parent
67e817490e
commit
a27daafa0a
@ -11,7 +11,7 @@ personal\_letter.
|
|||||||
|
|
||||||
The class looks as follows:
|
The class looks as follows:
|
||||||
|
|
||||||
|
:::latex
|
||||||
%
|
%
|
||||||
% personal_letter
|
% personal_letter
|
||||||
%
|
%
|
||||||
@ -110,17 +110,15 @@ The class looks as follows:
|
|||||||
{\large \bf #1}
|
{\large \bf #1}
|
||||||
|
|
||||||
}
|
}
|
||||||
{: .prettyprint .lang-tex}
|
|
||||||
|
|
||||||
Plain and simple.
|
Plain and simple.
|
||||||
|
|
||||||
And a usage example:
|
And a usage example:
|
||||||
|
|
||||||
|
:::latex
|
||||||
\documentclass{personal_letter}
|
\documentclass{personal_letter}
|
||||||
|
|
||||||
\name{Thomas Lovén}
|
|
||||||
\place{Göteborg}
|
\place{Göteborg}
|
||||||
\date{6 april 2010}
|
|
||||||
\adress{Thomas Lovén \\ Xxxxxxxxxxxxxxx XX \\ XXX XX Xxxxxx Xxxxxxxx}
|
\adress{Thomas Lovén \\ Xxxxxxxxxxxxxxx XX \\ XXX XX Xxxxxx Xxxxxxxx}
|
||||||
\telephone{+XX XX XXX XX XX}
|
\telephone{+XX XX XXX XX XX}
|
||||||
\email{thomasloven@gmail.com}
|
\email{thomasloven@gmail.com}
|
||||||
@ -160,6 +158,5 @@ And a usage example:
|
|||||||
\end{body}
|
\end{body}
|
||||||
|
|
||||||
\end{document}
|
\end{document}
|
||||||
{: .prettyprint .lang-tex}
|
|
||||||
|
|
||||||

|

|
||||||
|
@ -11,32 +11,30 @@ option enabled and one that runs entirely in a command line environment.
|
|||||||
|
|
||||||
Let's go!
|
Let's go!
|
||||||
|
|
||||||
curl -L -O http://sourceforge.net/projects/bochs/files/bochs/2.5.1/bochs-2.5.1.tar.gz
|
$ curl -L -O http://sourceforge.net/projects/bochs/files/bochs/2.5.1/bochs-2.5.1.tar.gz
|
||||||
tar -zxvf bochs-2.5.1.tar.gz
|
$ tar -zxvf bochs-2.5.1.tar.gz
|
||||||
mkdir build-bochs
|
$ mkdir build-bochs
|
||||||
cd build-bochs
|
$ cd build-bochs
|
||||||
|
$ ../bochs-2.5.1/configure --enable-smp --enable-cpu-level=6 --enable-all-optimizations --enable-pci --enable-vmx --enable-logging --enable-fpu --enable-sb16=dummy --enable-cdrom --disable-plugins --disable-docbook --with-term
|
||||||
|
$ make all
|
||||||
|
$ make install
|
||||||
|
|
||||||
../bochs-2.5.1/configure --enable-smp --enable-cpu-level=6 --enable-all-optimizations --enable-pci --enable-vmx --enable-logging --enable-fpu --enable-sb16=dummy --enable-cdrom --disable-plugins --disable-docbook --with-term
|
And that's the terminal version.
|
||||||
make all
|
|
||||||
make install
|
|
||||||
{: .prettyprint .lan-sh}
|
|
||||||
|
|
||||||
And that's the terminal version. ### Important note This configure line won't
|
### Important note
|
||||||
actually work. I'll update with the new one as soon as I can.
|
This configure line won't actually work. I'll update with the new one as soon as
|
||||||
|
I can.
|
||||||
|
|
||||||
Now we move this to *bochs-term* and compile the version with the debugger.
|
Now we move this to *bochs-term* and compile the version with the debugger.
|
||||||
|
|
||||||
mv /usr/local/bin/bochs /usr/local/bin/bochs-term
|
$ mv /usr/local/bin/bochs /usr/local/bin/bochs-term
|
||||||
|
$ rm *
|
||||||
rm *
|
$ ../bochs-2.5.1/configure --enable-smp --enable-cpu-level=6 --enable-all-optimizations --enable-pci --enable-vmx --enable-logging --enable-fpu --enable-sb16=dummy --enable-cdrom --disable-plugins --disable-docbook --enable-debugger --enable-disasm --with-x11
|
||||||
../bochs-2.5.1/configure --enable-smp --enable-cpu-level=6 --enable-all-optimizations --enable-pci --enable-vmx --enable-logging --enable-fpu --enable-sb16=dummy --enable-cdrom --disable-plugins --disable-docbook --enable-debugger --enable-disasm --with-x11
|
$ make all
|
||||||
make all
|
$ make install
|
||||||
make install
|
|
||||||
{: .prettyprint .lan-sh}
|
|
||||||
|
|
||||||
Finally, copy the bochs bioses to a public place
|
Finally, copy the bochs bioses to a public place
|
||||||
|
|
||||||
cp -r ../bochs-2.5.1/bios /usr/share/bochs
|
$ cp -r ../bochs-2.5.1/bios /usr/share/bochs
|
||||||
{: .prettyprint .lan-sh}
|
|
||||||
|
|
||||||
And that's it.
|
And that's it.
|
||||||
|
@ -21,18 +21,17 @@ c-compiler. An update was required on one of my computers.
|
|||||||
Compiling gcc also requires the mpfr package to be installed. This I did with
|
Compiling gcc also requires the mpfr package to be installed. This I did with
|
||||||
[Homebrew](http://mxcl.github.com/homebrew/).
|
[Homebrew](http://mxcl.github.com/homebrew/).
|
||||||
|
|
||||||
brew install mpfr
|
:::bash
|
||||||
{: .prettyprint}
|
$ brew install mpfr
|
||||||
|
|
||||||
I downloaded all the sources I needed from [gnu.org](http://gnu.org).
|
I downloaded all the sources I needed from [gnu.org](http://gnu.org).
|
||||||
|
|
||||||
curl -O http://ftp.gnu.org/gnu/binutils/binutils-2.22.tar.gz
|
:::bash
|
||||||
curl -O http://ftp.gnu.org/gnu/gcc/gcc-4.6.3/gcc-core-4.6.3.tar.gz
|
$ curl -O http://ftp.gnu.org/gnu/binutils/binutils-2.22.tar.gz
|
||||||
|
$ curl -O http://ftp.gnu.org/gnu/gcc/gcc-4.6.3/gcc-core-4.6.3.tar.gz
|
||||||
curl -O http://ftp.gnu.org/gnu/gmp/gmp-5.0.2.tar.gz
|
$ curl -O http://ftp.gnu.org/gnu/gmp/gmp-5.0.2.tar.gz
|
||||||
curl -O http://ftp.gnu.org/gnu/mpfr/mpfr-3.1.0.tar.gz
|
$ curl -O http://ftp.gnu.org/gnu/mpfr/mpfr-3.1.0.tar.gz
|
||||||
curl -O http://www.multiprecision.org/mpc/download/mpc-0.9.tar.gz
|
$ curl -O http://www.multiprecision.org/mpc/download/mpc-0.9.tar.gz
|
||||||
{: .prettyprint .lang-bash}
|
|
||||||
|
|
||||||
Feel free to use later versions, but if you do, I cannot guarantee that the
|
Feel free to use later versions, but if you do, I cannot guarantee that the
|
||||||
code posted in my logs will work for you (it's very likely to work, but not
|
code posted in my logs will work for you (it's very likely to work, but not
|
||||||
@ -40,33 +39,32 @@ code posted in my logs will work for you (it's very likely to work, but not
|
|||||||
gcc, so after extracting all archives, they are simply copied into the gcc
|
gcc, so after extracting all archives, they are simply copied into the gcc
|
||||||
source
|
source
|
||||||
|
|
||||||
mv gmp-5.0.2 gcc-4.6.3/gmp
|
:::bash
|
||||||
mv mpfr-3.1.0 gcc-4.6.3/mpfr
|
$ mv gmp-5.0.2 gcc-4.6.3/gmp
|
||||||
mv mpc-0.9 gcc-4.6.3/mpc
|
$ mv mpfr-3.1.0 gcc-4.6.3/mpfr
|
||||||
{: .prettyprint .lang-sh}
|
$ mv mpc-0.9 gcc-4.6.3/mpc
|
||||||
|
|
||||||
In order not to mess up the source, binutils and gcc were built out of tree.
|
In order not to mess up the source, binutils and gcc were built out of tree.
|
||||||
|
|
||||||
mkdir build-binutils
|
:::bash
|
||||||
cd build-binutils
|
$ mkdir build-binutils
|
||||||
|
$ cd build-binutils
|
||||||
export PREFIX=/usr/local/cross
|
$ export PREFIX=/usr/local/cross
|
||||||
export TARGET=i386-elf
|
$ export TARGET=i386-elf
|
||||||
../binutils-2.22/configure --target=$TARGET --prefix=$PREFIX --disable-nls
|
$ ../binutils-2.22/configure --target=$TARGET --prefix=$PREFIX --disable-nls
|
||||||
make all
|
$ make all
|
||||||
make install
|
$ make install
|
||||||
{: .prettyprint .lang-bash}
|
|
||||||
|
|
||||||
And the same for gcc, using the new binutils
|
And the same for gcc, using the new binutils
|
||||||
|
|
||||||
cd ..
|
:::bash
|
||||||
mkdir build-gcc
|
$ cd ..
|
||||||
cd build-gcc
|
$ mkdir build-gcc
|
||||||
export PATH=$PATH:$PREFIX/bin
|
$ cd build-gcc
|
||||||
../gcc-4.6.3/configure --target=$TARGET --prefix=$PREFIX --disable-nls --enable-languages=c --without-headers
|
$ export PATH=$PATH:$PREFIX/bin
|
||||||
make all-gcc
|
$ ../gcc-4.6.3/configure --target=$TARGET --prefix=$PREFIX --disable-nls --enable-languages=c --without-headers
|
||||||
make install-gcc
|
$ make all-gcc
|
||||||
{: .prettyprint .lang-bash}
|
$ make install-gcc
|
||||||
|
|
||||||
It's really important to run _make all-gcc_ and _make install-gcc_ and __not__
|
It's really important to run _make all-gcc_ and _make install-gcc_ and __not__
|
||||||
_make all_ and _make install_ here. It probably works anyway - if you ever
|
_make all_ and _make install_ here. It probably works anyway - if you ever
|
||||||
|
@ -39,6 +39,7 @@ the main makefile is to run the makefile in kernel/, copy the kernel image into
|
|||||||
the floppy and then run bochs-term.
|
the floppy and then run bochs-term.
|
||||||
|
|
||||||
|
|
||||||
|
:::make
|
||||||
BUILDDIR := $(PWD)
|
BUILDDIR := $(PWD)
|
||||||
|
|
||||||
PATH := /usr/local/cross/bin:$(PATH)
|
PATH := /usr/local/cross/bin:$(PATH)
|
||||||
@ -83,11 +84,11 @@ the floppy and then run bochs-term.
|
|||||||
|
|
||||||
force:
|
force:
|
||||||
true
|
true
|
||||||
{: .prettyprint}
|
|
||||||
|
|
||||||
The makefile in the kernel/ directory is pretty much straight forward.
|
The makefile in the kernel/ directory is pretty much straight forward.
|
||||||
|
|
||||||
|
|
||||||
|
:::make
|
||||||
TARGET := kernel
|
TARGET := kernel
|
||||||
SUBDIR := kernel
|
SUBDIR := kernel
|
||||||
|
|
||||||
@ -117,4 +118,3 @@ The makefile in the kernel/ directory is pretty much straight forward.
|
|||||||
clean:
|
clean:
|
||||||
-@rm $(SOURCES) 2>/dev/null
|
-@rm $(SOURCES) 2>/dev/null
|
||||||
-@rm $(TARGET) 2>/dev/null
|
-@rm $(TARGET) 2>/dev/null
|
||||||
{: .prettyprint}
|
|
||||||
|
@ -46,7 +46,6 @@ a special Linker file for the kernel.
|
|||||||
|
|
||||||
_end = .;
|
_end = .;
|
||||||
}
|
}
|
||||||
{: .prettyprint .linenums}
|
|
||||||
|
|
||||||
GRUB drops us off at the kernel entry point - *start* as defined in the
|
GRUB drops us off at the kernel entry point - *start* as defined in the
|
||||||
linkfile - without paging and with an unknown GDT. So setting that up will be
|
linkfile - without paging and with an unknown GDT. So setting that up will be
|
||||||
@ -54,6 +53,7 @@ the first order of business. This is done in the assembly bootstrap.
|
|||||||
|
|
||||||
*kernel/boot.s*
|
*kernel/boot.s*
|
||||||
|
|
||||||
|
:::nasm
|
||||||
; Kernel start point
|
; Kernel start point
|
||||||
[global start]
|
[global start]
|
||||||
start:
|
start:
|
||||||
@ -77,7 +77,6 @@ the first order of business. This is done in the assembly bootstrap.
|
|||||||
jmp 0x8:.gdtLoaded
|
jmp 0x8:.gdtLoaded
|
||||||
|
|
||||||
.gdtLoaded:
|
.gdtLoaded:
|
||||||
{: .prettyprint .lang-nasm .linenums:61}
|
|
||||||
|
|
||||||
Here's another new thing for me. Macros. Can't believe I could do without them
|
Here's another new thing for me. Macros. Can't believe I could do without them
|
||||||
before. *SetSegments* is a macro that in this case loads 0x10 into ecx and then
|
before. *SetSegments* is a macro that in this case loads 0x10 into ecx and then
|
||||||
@ -86,6 +85,7 @@ which also contains some constants.
|
|||||||
|
|
||||||
*kernel/include/asm_macros.inc*
|
*kernel/include/asm_macros.inc*
|
||||||
|
|
||||||
|
:::nasm
|
||||||
; GRUB multiboot headers
|
; GRUB multiboot headers
|
||||||
MBOOT_PAGE_ALIGNED_FLAG equ 1<<0
|
MBOOT_PAGE_ALIGNED_FLAG equ 1<<0
|
||||||
MBOOT_MEMORY_INFO_FLAG equ 1<<1
|
MBOOT_MEMORY_INFO_FLAG equ 1<<1
|
||||||
@ -107,14 +107,13 @@ which also contains some constants.
|
|||||||
mov gs, %2
|
mov gs, %2
|
||||||
mov ss, %2
|
mov ss, %2
|
||||||
%endmacro
|
%endmacro
|
||||||
{: .prettyprint .lang-nasm .linenums:2}
|
|
||||||
|
|
||||||
There are also references to some data structures, i.e. *BootPageDirectory* and
|
There are also references to some data structures, i.e. *BootPageDirectory* and
|
||||||
*gdt_ptr*. Those are hardcoded in the bootstrap file.
|
*gdt_ptr*. Those are hardcoded in the bootstrap file.
|
||||||
|
|
||||||
*kernel/boot.s*
|
*kernel/boot.s*
|
||||||
|
|
||||||
|
:::nasm
|
||||||
%include "include/asm_macros.inc"
|
%include "include/asm_macros.inc"
|
||||||
|
|
||||||
[bits 32]
|
[bits 32]
|
||||||
@ -173,7 +172,6 @@ There are also references to some data structures, i.e. *BootPageDirectory* and
|
|||||||
dd MBOOT_HEADER_MAGIC
|
dd MBOOT_HEADER_MAGIC
|
||||||
dd MBOOT_HEADER_FLAGS
|
dd MBOOT_HEADER_FLAGS
|
||||||
dd MBOOT_HEADER_CHECKSUM
|
dd MBOOT_HEADER_CHECKSUM
|
||||||
{: .prettyprint .lang-nasm .linenums:2}
|
|
||||||
|
|
||||||
Well. This is first of all some area saved for a stack. Then there's the page
|
Well. This is first of all some area saved for a stack. Then there's the page
|
||||||
directory which has the same page table at virtual memory 0x00000000 and
|
directory which has the same page table at virtual memory 0x00000000 and
|
||||||
@ -191,6 +189,7 @@ from the page directory.
|
|||||||
|
|
||||||
*kernel/boot.s*
|
*kernel/boot.s*
|
||||||
|
|
||||||
|
:::nasm
|
||||||
.gdtLoaded:
|
.gdtLoaded:
|
||||||
; Clear the identity mapping from the page directory
|
; Clear the identity mapping from the page directory
|
||||||
mov edx, BootPageDirectory
|
mov edx, BootPageDirectory
|
||||||
@ -213,7 +212,6 @@ from the page directory.
|
|||||||
[extern kinit]
|
[extern kinit]
|
||||||
call kinit
|
call kinit
|
||||||
jmp $
|
jmp $
|
||||||
{: .prettyprint .lang-nasm .linenums:83}
|
|
||||||
|
|
||||||
The final thing we do before jumping into the c kernel stub is push the values
|
The final thing we do before jumping into the c kernel stub is push the values
|
||||||
of eax and ebx which contains the multiboot magic number and information
|
of eax and ebx which contains the multiboot magic number and information
|
||||||
@ -223,12 +221,11 @@ The only thing that's left now in order to get this to run is the c stub.
|
|||||||
|
|
||||||
*kernel/kinit.c*
|
*kernel/kinit.c*
|
||||||
|
|
||||||
|
:::c
|
||||||
void kinit()
|
void kinit()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
{: .prettyprint .linenums:2}
|
|
||||||
|
|
||||||
Compiling and running this through bochs, we are presented with a black and
|
Compiling and running this through bochs, we are presented with a black and
|
||||||
white screen completely void of error messages. Perfect!
|
white screen completely void of error messages. Perfect!
|
||||||
|
@ -6,13 +6,14 @@ tags: [osdev]
|
|||||||
Something that always annoyed me is how hard it is to synchronize constants
|
Something that always annoyed me is how hard it is to synchronize constants
|
||||||
between assembly and c code. In assembler, you define a constant value as
|
between assembly and c code. In assembler, you define a constant value as
|
||||||
|
|
||||||
|
:::nasm
|
||||||
EXACT_PI equ 3
|
EXACT_PI equ 3
|
||||||
{: .prettyprint .lang-nasm}
|
|
||||||
|
|
||||||
and in c
|
and in c
|
||||||
|
|
||||||
|
:::c
|
||||||
#define EXACT_PI 3
|
#define EXACT_PI 3
|
||||||
{: .prettyprint .lang-c}
|
|
||||||
As is usually the case with things that annoy me, there is of course a solution
|
As is usually the case with things that annoy me, there is of course a solution
|
||||||
to this, as I found out today. The solution is the c preprocessor.
|
to this, as I found out today. The solution is the c preprocessor.
|
||||||
|
|
||||||
@ -30,13 +31,14 @@ Well, here's a minimal (non-working) example:
|
|||||||
|
|
||||||
_myAsmFile.asm_
|
_myAsmFile.asm_
|
||||||
|
|
||||||
|
:::nasm
|
||||||
#include <header.h>
|
#include <header.h>
|
||||||
|
|
||||||
mov eax, EXACT_PI
|
mov eax, EXACT_PI
|
||||||
{: .prettyprint .lang-nasm}
|
|
||||||
|
|
||||||
_include/header.h_
|
_include/header.h_
|
||||||
|
|
||||||
|
:::c
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define EXACT_PI 3
|
#define EXACT_PI 3
|
||||||
@ -44,13 +46,12 @@ _include/header.h_
|
|||||||
#ifndef __ASSEMBLER__
|
#ifndef __ASSEMBLER__
|
||||||
// This is not evaluated if header.h is included from an assembly file.
|
// This is not evaluated if header.h is included from an assembly file.
|
||||||
#endif
|
#endif
|
||||||
{: .prettyprint .lang-c}
|
|
||||||
|
|
||||||
This is compiled through:
|
This is compiled through:
|
||||||
|
|
||||||
cpp -I include -x assembler-with-cpp myAsmFile.asm -o myAsmFile.s
|
:::bash
|
||||||
nasm myAsmFile.s
|
$ cpp -I include -x assembler-with-cpp myAsmFile.asm -o myAsmFile.s
|
||||||
{: .prettyprint}
|
$ nasm myAsmFile.s
|
||||||
|
|
||||||
The _-x_-flag tells the preprocessor what type of file the following input
|
The _-x_-flag tells the preprocessor what type of file the following input
|
||||||
files are. _assembler-with-cpp_ means _cpp_ will ignore everything but the
|
files are. _assembler-with-cpp_ means _cpp_ will ignore everything but the
|
||||||
|
@ -32,16 +32,21 @@ that each page only has room for four pointers. The figure below shows eight
|
|||||||
physical pages of a such computer. The two leftmost pages are used by the
|
physical pages of a such computer. The two leftmost pages are used by the
|
||||||
physical memory manager for the free page stack. The stack contains pointers to
|
physical memory manager for the free page stack. The stack contains pointers to
|
||||||
the next five pages, who are free. The rightmost page is handed out.
|
the next five pages, who are free. The rightmost page is handed out.
|
||||||
|
|
||||||
{: .noborder .center}
|
{: .noborder .center}
|
||||||
|
|
||||||
When the _pmm_ receives a request for a memory page it will pop the topmost
|
When the _pmm_ receives a request for a memory page it will pop the topmost
|
||||||
entry from the stack and returns, in this case, the second rightmost page to
|
entry from the stack and returns, in this case, the second rightmost page to
|
||||||
the caller. {: .noborder .center}
|
the caller.
|
||||||
|
|
||||||
|
{: .noborder .center}
|
||||||
|
|
||||||
The next time the pmm receives a request for a memory page it will notice that
|
The next time the pmm receives a request for a memory page it will notice that
|
||||||
an entire page of the stack is empty and just being wasted, so it will shrink
|
an entire page of the stack is empty and just being wasted, so it will shrink
|
||||||
its stack by one page-size and return the address of the page that previously
|
its stack by one page-size and return the address of the page that previously
|
||||||
made up the top of the stack. {: .noborder .center}
|
made up the top of the stack
|
||||||
|
|
||||||
|
{: .noborder .center}
|
||||||
|
|
||||||
Likewise, if the stack is full of pointers when a used page is handed back,
|
Likewise, if the stack is full of pointers when a used page is handed back,
|
||||||
that page is used to increase the stack space. Through the use of virtual
|
that page is used to increase the stack space. Through the use of virtual
|
||||||
|
@ -54,7 +54,9 @@ looking up the fourth group in the page directory. It finds the address of the
|
|||||||
page directory and assumes this is the page table for the group. It caries on,
|
page directory and assumes this is the page table for the group. It caries on,
|
||||||
looking up the second entry in this 'page table' and gets the address of the
|
looking up the second entry in this 'page table' and gets the address of the
|
||||||
page it wants. This happens to be the address of the page table for the second
|
page it wants. This happens to be the address of the page table for the second
|
||||||
group. {: .center .noborder}
|
group.
|
||||||
|
|
||||||
|
{: .center .noborder}
|
||||||
|
|
||||||
In other words, you can access any page table through a fixed address in
|
In other words, you can access any page table through a fixed address in
|
||||||
memory. But wait, it gets even better.
|
memory. But wait, it gets even better.
|
||||||
@ -64,7 +66,9 @@ virtual memory. The MMU will look up the last entry in the page directory and
|
|||||||
get the address of the page directory. It will then look up the last entry in
|
get the address of the page directory. It will then look up the last entry in
|
||||||
the page directory and get the address of the page directory (that's not a typo
|
the page directory and get the address of the page directory (that's not a typo
|
||||||
- I meant to write the same thing twice). This lets you access the page
|
- I meant to write the same thing twice). This lets you access the page
|
||||||
directory too through a fixed memory address. {: .center .noborder}
|
directory too through a fixed memory address.
|
||||||
|
|
||||||
|
{: .center .noborder}
|
||||||
|
|
||||||
###Some considerations
|
###Some considerations
|
||||||
An important question to put at this point is whether a recursive page
|
An important question to put at this point is whether a recursive page
|
||||||
@ -91,6 +95,7 @@ tool, others don't.
|
|||||||
Finally, if a recursive page directory is used on an x86, the following can be
|
Finally, if a recursive page directory is used on an x86, the following can be
|
||||||
used to access the page directories and tables:
|
used to access the page directories and tables:
|
||||||
|
|
||||||
|
:::c
|
||||||
uint32_t *page_dir = 0xFFFFF000;
|
uint32_t *page_dir = 0xFFFFF000;
|
||||||
uint32_t *page_tables = 0xFFC00000;
|
uint32_t *page_tables = 0xFFC00000;
|
||||||
|
|
||||||
@ -100,7 +105,6 @@ used to access the page directories and tables:
|
|||||||
|
|
||||||
page_dir[addr >> 22] = &page_tables[addr >> 12] | flags;
|
page_dir[addr >> 22] = &page_tables[addr >> 12] | flags;
|
||||||
page_tables[addr >> 12] = phys | flags;
|
page_tables[addr >> 12] = phys | flags;
|
||||||
{:.prettyprint}
|
|
||||||
|
|
||||||
###Git
|
###Git
|
||||||
A recursive page directory has been implemented in Git commit
|
A recursive page directory has been implemented in Git commit
|
||||||
|
@ -26,6 +26,7 @@ Now, let's look at a few ways to do this.
|
|||||||
The simplest possible memory manager just hands out a new address each time
|
The simplest possible memory manager just hands out a new address each time
|
||||||
_malloc_ is called and doesn't care when memory is freed.
|
_malloc_ is called and doesn't care when memory is freed.
|
||||||
|
|
||||||
|
:::c
|
||||||
uint32_t memory_pointer = HEAP_START;
|
uint32_t memory_pointer = HEAP_START;
|
||||||
|
|
||||||
void *malloc(uint32_t size);
|
void *malloc(uint32_t size);
|
||||||
@ -38,7 +39,6 @@ _malloc_ is called and doesn't care when memory is freed.
|
|||||||
{
|
{
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
{: .prettyprint}
|
|
||||||
|
|
||||||
###Heap
|
###Heap
|
||||||
The next method - which I prefer - is a memory heap.
|
The next method - which I prefer - is a memory heap.
|
||||||
@ -46,6 +46,7 @@ The next method - which I prefer - is a memory heap.
|
|||||||
The heap consists of a list of free memory areas. In the simplest possible
|
The heap consists of a list of free memory areas. In the simplest possible
|
||||||
variety, it would look something like this:
|
variety, it would look something like this:
|
||||||
|
|
||||||
|
:::c
|
||||||
struct area_header
|
struct area_header
|
||||||
{
|
{
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
@ -72,7 +73,6 @@ variety, it would look something like this:
|
|||||||
struct area_header area = get_area_header(mem);
|
struct area_header area = get_area_header(mem);
|
||||||
insert_into_heap_list(area);
|
insert_into_heap_list(area);
|
||||||
}
|
}
|
||||||
{: .prettyprint}
|
|
||||||
|
|
||||||
Here it is assumed that the free memory is already divided into smaller areas
|
Here it is assumed that the free memory is already divided into smaller areas
|
||||||
that each start with an *area_header* structure as in the figure below. If the
|
that each start with an *area_header* structure as in the figure below. If the
|
||||||
|
@ -7,8 +7,8 @@ tags: [osdev]
|
|||||||
Today I was writing some code for handling interrupts.
|
Today I was writing some code for handling interrupts.
|
||||||
At one point I needed the following piece of code
|
At one point I needed the following piece of code
|
||||||
|
|
||||||
|
:::c
|
||||||
extern void isr0(void), isr1(void), isr2(void), isr3(void), isr4(void), isr5(void), isr6(void), isr7(void), isr8(void), isr9(void), isr10(void), isr11(void), isr12(void), isr13(void), isr14(void), isr15(void), isr16(void), isr17(void), isr18(void), isr19(void), isr20(void), isr21(void), isr22(void), isr23(void), isr24(void), isr25(void), isr26(void), isr27(void), isr28(void), isr29(void), isr30(void), isr31(void), isr32(void), isr33(void), isr34(void), isr35(void), isr36(void), isr37(void), isr38(void), isr39(void), isr40(void), isr41(void), isr42(void), isr43(void), isr44(void), isr45(void), isr46(void), isr47(void);
|
extern void isr0(void), isr1(void), isr2(void), isr3(void), isr4(void), isr5(void), isr6(void), isr7(void), isr8(void), isr9(void), isr10(void), isr11(void), isr12(void), isr13(void), isr14(void), isr15(void), isr16(void), isr17(void), isr18(void), isr19(void), isr20(void), isr21(void), isr22(void), isr23(void), isr24(void), isr25(void), isr26(void), isr27(void), isr28(void), isr29(void), isr30(void), isr31(void), isr32(void), isr33(void), isr34(void), isr35(void), isr36(void), isr37(void), isr38(void), isr39(void), isr40(void), isr41(void), isr42(void), isr43(void), isr44(void), isr45(void), isr46(void), isr47(void);
|
||||||
{: .prettyprint}
|
|
||||||
|
|
||||||
###The solution
|
###The solution
|
||||||
Vim macros.
|
Vim macros.
|
||||||
@ -53,8 +53,8 @@ The next part:
|
|||||||
runs the macro 46 times, steps up 47 times and joins the current line with the
|
runs the macro 46 times, steps up 47 times and joins the current line with the
|
||||||
next 48 times. We now have
|
next 48 times. We now have
|
||||||
|
|
||||||
|
:::c
|
||||||
isr0(void), isr1(void), isr2(void), isr3(void), isr4(void), isr5(void), isr6(void), isr7(void), isr8(void), isr9(void), isr10(void), isr11(void), isr12(void), isr13(void), isr14(void), isr15(void), isr16(void), isr17(void), isr18(void), isr19(void), isr20(void), isr21(void), isr22(void), isr23(void), isr24(void), isr25(void), isr26(void), isr27(void), isr28(void), isr29(void), isr30(void), isr31(void), isr32(void), isr33(void), isr34(void), isr35(void), isr36(void), isr37(void), isr38(void), isr39(void), isr40(void), isr41(void), isr42(void), isr43(void), isr44(void), isr45(void), isr46(void),
|
isr0(void), isr1(void), isr2(void), isr3(void), isr4(void), isr5(void), isr6(void), isr7(void), isr8(void), isr9(void), isr10(void), isr11(void), isr12(void), isr13(void), isr14(void), isr15(void), isr16(void), isr17(void), isr18(void), isr19(void), isr20(void), isr21(void), isr22(void), isr23(void), isr24(void), isr25(void), isr26(void), isr27(void), isr28(void), isr29(void), isr30(void), isr31(void), isr32(void), isr33(void), isr34(void), isr35(void), isr36(void), isr37(void), isr38(void), isr39(void), isr40(void), isr41(void), isr42(void), isr43(void), isr44(void), isr45(void), isr46(void),
|
||||||
{: .prettyprint}
|
|
||||||
|
|
||||||
and all we need to do now is replace the last comma with a semicolon using
|
and all we need to do now is replace the last comma with a semicolon using
|
||||||
_$r;_ and insert _extern void_ at the beginning of the line using _I_.
|
_$r;_ and insert _extern void_ at the beginning of the line using _I_.
|
||||||
@ -63,8 +63,8 @@ _$r;_ and insert _extern void_ at the beginning of the line using _I_.
|
|||||||
|
|
||||||
Starting with
|
Starting with
|
||||||
|
|
||||||
|
:::nasm
|
||||||
INTNOERR 0
|
INTNOERR 0
|
||||||
{: .prettyprint}
|
|
||||||
|
|
||||||
I used
|
I used
|
||||||
|
|
||||||
@ -74,6 +74,7 @@ I used
|
|||||||
|
|
||||||
and ended up with
|
and ended up with
|
||||||
|
|
||||||
|
:::nasm
|
||||||
INTNOERR 0
|
INTNOERR 0
|
||||||
INTNOERR 1
|
INTNOERR 1
|
||||||
INTNOERR 2
|
INTNOERR 2
|
||||||
@ -93,7 +94,6 @@ and ended up with
|
|||||||
...
|
...
|
||||||
INTNOERR 45
|
INTNOERR 45
|
||||||
INTNOERR 46
|
INTNOERR 46
|
||||||
{: .prettyprint}
|
|
||||||
|
|
||||||
I love vim!
|
I love vim!
|
||||||
|
|
||||||
|
@ -14,16 +14,16 @@ popular. With segmentation, the physical memory is divided into segments that
|
|||||||
work as a kind of translation table. In Protected mode, if you call an address
|
work as a kind of translation table. In Protected mode, if you call an address
|
||||||
like
|
like
|
||||||
|
|
||||||
|
:::nasm
|
||||||
jmp CS:AX
|
jmp CS:AX
|
||||||
{: .prettyprint .lang-nasm}
|
|
||||||
|
|
||||||
the processor looks into the currently loaded __Local__ or __Global Descriptor
|
the processor looks into the currently loaded __Local__ or __Global Descriptor
|
||||||
Table__ ( __LDT__/ __GDT__) for the entry pointed to by _CS_. This enty (or
|
Table__ ( __LDT__/ __GDT__) for the entry pointed to by _CS_. This enty (or
|
||||||
__Segment Descriptor__) describes the beginning of a segment which is combined
|
__Segment Descriptor__) describes the beginning of a segment which is combined
|
||||||
with the offset in _AX_ to get the physical address;
|
with the offset in _AX_ to get the physical address;
|
||||||
|
|
||||||
|
:::c
|
||||||
physical_address = segment_descriptor_from_index(CS).base + AX;
|
physical_address = segment_descriptor_from_index(CS).base + AX;
|
||||||
{: .prettyprint}
|
|
||||||
|
|
||||||
The segment descriptor also has a limit, which in our example is the maximum
|
The segment descriptor also has a limit, which in our example is the maximum
|
||||||
value _AX_ is allowed to take. If it's higher, you get a __Segmentation Fault__
|
value _AX_ is allowed to take. If it's higher, you get a __Segmentation Fault__
|
||||||
@ -76,11 +76,11 @@ Changing the CPL is actually two different problems.
|
|||||||
|
|
||||||
Increasing the CPL is relatively easy. It can be done either through a far jump
|
Increasing the CPL is relatively easy. It can be done either through a far jump
|
||||||
|
|
||||||
|
:::nasm
|
||||||
JMP 0x1B:label
|
JMP 0x1B:label
|
||||||
label:
|
label:
|
||||||
; The CS selector is now 0x18 | 0x3
|
; The CS selector is now 0x18 | 0x3
|
||||||
; i.e. it points to segment no 3 (3*0x8) and CPL is set to 0x3
|
; i.e. it points to segment no 3 (3*0x8) and CPL is set to 0x3
|
||||||
{: .prettyprint .lang-nasm}
|
|
||||||
|
|
||||||
or through the `IRET` instruction
|
or through the `IRET` instruction
|
||||||
|
|
||||||
@ -119,6 +119,7 @@ bottom two bits to 0x3, you will soon be in User Mode.
|
|||||||
An other (better in my opinion) option is to create a fake interrupt-pushed
|
An other (better in my opinion) option is to create a fake interrupt-pushed
|
||||||
stack and push that onto the stack before running `IRET` .
|
stack and push that onto the stack before running `IRET` .
|
||||||
|
|
||||||
|
:::c
|
||||||
// C code
|
// C code
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
@ -137,14 +138,14 @@ stack and push that onto the stack before running `IRET` .
|
|||||||
|
|
||||||
set_all_segments(user_data_segment | 0x3);
|
set_all_segments(user_data_segment | 0x3);
|
||||||
run_iret(&fake_stack);
|
run_iret(&fake_stack);
|
||||||
{: .prettyprint}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:::nasm
|
||||||
; Assembler code
|
; Assembler code
|
||||||
run_iret:
|
run_iret:
|
||||||
add esp, 0x8
|
add esp, 0x8
|
||||||
iret
|
iret
|
||||||
{: .prettyprint .lang-nasm}
|
|
||||||
|
|
||||||
|
|
||||||
###Going back to ring0
|
###Going back to ring0
|
||||||
|
|
||||||
|
@ -53,11 +53,11 @@ hard in fact that most tutorials actually do. Here's how it's really done
|
|||||||
(source: [Intel
|
(source: [Intel
|
||||||
Manuals](http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html/))
|
Manuals](http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html/))
|
||||||
|
|
||||||
|
:::c
|
||||||
gdt[TSS_DESCRIPTOR].base = &tss;
|
gdt[TSS_DESCRIPTOR].base = &tss;
|
||||||
gdt[TSS_DESCRIPTOR].limit = sizeof(tss);
|
gdt[TSS_DESCRIPTOR].limit = sizeof(tss);
|
||||||
gdt[TSS_DESCRIPTOR].flags = 0;
|
gdt[TSS_DESCRIPTOR].flags = 0;
|
||||||
gdt[TSS_DESCRIPTOR].access = GDT_PRESENT | GDT_EXECUTABLE | GDT_ACCESSED;
|
gdt[TSS_DESCRIPTOR].access = GDT_PRESENT | GDT_EXECUTABLE | GDT_ACCESSED;
|
||||||
{: .prettyprint}
|
|
||||||
|
|
||||||
What most tutorials get wrong is the limit field. The TSS is actually a memory
|
What most tutorials get wrong is the limit field. The TSS is actually a memory
|
||||||
segment, and like all memory segments it has a segment descriptor in the GDT
|
segment, and like all memory segments it has a segment descriptor in the GDT
|
||||||
|
@ -39,6 +39,7 @@ Since the return address is stored on the stack, if you were to switch
|
|||||||
stacks inside a function, when you return, you'll be somewhere else.
|
stacks inside a function, when you return, you'll be somewhere else.
|
||||||
This is a common way of making usermode threads. Ponder the following:
|
This is a common way of making usermode threads. Ponder the following:
|
||||||
|
|
||||||
|
:::c
|
||||||
void switch_thread()
|
void switch_thread()
|
||||||
{
|
{
|
||||||
push_all_registers();
|
push_all_registers();
|
||||||
@ -75,7 +76,7 @@ running. The top of the stacks looks like:
|
|||||||
+----------ESP----------+ |switch_thread RA |
|
+----------ESP----------+ |switch_thread RA |
|
||||||
|a RA | |b RA |
|
|a RA | |b RA |
|
||||||
| ... | | ... |
|
| ... | | ... |
|
||||||
{: .nopretty}
|
|
||||||
where `RA` means Return Address and `ESP` is where the stack pointer is
|
where `RA` means Return Address and `ESP` is where the stack pointer is
|
||||||
currently pointing.
|
currently pointing.
|
||||||
As execution of __A__ continues, the processor will `do_something()` and
|
As execution of __A__ continues, the processor will `do_something()` and
|
||||||
@ -87,7 +88,7 @@ then call `switch_thread()`...
|
|||||||
|switch_thread RA | |switch_thread RA |
|
|switch_thread RA | |switch_thread RA |
|
||||||
|a RA | |b RA |
|
|a RA | |b RA |
|
||||||
| ... | | ... |
|
| ... | | ... |
|
||||||
{: .nopretty}
|
|
||||||
`switch_thread()` pushes all registers to the stack and calls
|
`switch_thread()` pushes all registers to the stack and calls
|
||||||
`switch_stack_pointer()`
|
`switch_stack_pointer()`
|
||||||
|
|
||||||
@ -97,7 +98,7 @@ then call `switch_thread()`...
|
|||||||
|switch_thread RA | |switch_thread RA |
|
|switch_thread RA | |switch_thread RA |
|
||||||
|a RA | |b RA |
|
|a RA | |b RA |
|
||||||
| ... | | ... |
|
| ... | | ... |
|
||||||
{: .nopretty}
|
|
||||||
`switch_stack_pointer()` performs some scheduling to find out which
|
`switch_stack_pointer()` performs some scheduling to find out which
|
||||||
thread is to run next, and then switches the stack pointer over to the
|
thread is to run next, and then switches the stack pointer over to the
|
||||||
top of __B__'s stack.
|
top of __B__'s stack.
|
||||||
@ -108,7 +109,7 @@ top of __B__'s stack.
|
|||||||
|switch_thread RA | |switch_thread RA |
|
|switch_thread RA | |switch_thread RA |
|
||||||
|a RA | |b RA |
|
|a RA | |b RA |
|
||||||
| ... | | ... |
|
| ... | | ... |
|
||||||
{: .nopretty}
|
|
||||||
The processor keeps on executing code, and `switch_stack_pointer()` soon
|
The processor keeps on executing code, and `switch_stack_pointer()` soon
|
||||||
returns
|
returns
|
||||||
|
|
||||||
@ -118,7 +119,7 @@ returns
|
|||||||
|switch_thread RA | |switch_thread RA |
|
|switch_thread RA | |switch_thread RA |
|
||||||
|a RA | |b RA |
|
|a RA | |b RA |
|
||||||
| ... | | ... |
|
| ... | | ... |
|
||||||
{: .nopretty}
|
|
||||||
`switch_thread()` pops all registers and returns...
|
`switch_thread()` pops all registers and returns...
|
||||||
|
|
||||||
+-----------------------+
|
+-----------------------+
|
||||||
@ -127,7 +128,7 @@ returns
|
|||||||
|switch_thread RA | +----------ESP----------+
|
|switch_thread RA | +----------ESP----------+
|
||||||
|a RA | |b RA |
|
|a RA | |b RA |
|
||||||
| ... | | ... |
|
| ... | | ... |
|
||||||
{: .nopretty}
|
|
||||||
... and we're now in `b()` with all registers of __B__ loaded.
|
... and we're now in `b()` with all registers of __B__ loaded.
|
||||||
|
|
||||||
###Stacks in the kernel
|
###Stacks in the kernel
|
||||||
@ -144,6 +145,7 @@ Molloys](http://www.jamesmolloy.co.uk/tutorial_html/) or [Brandon
|
|||||||
Friesens](http://www.osdever.net/bkerndev/Docs/title.htm)) you probably
|
Friesens](http://www.osdever.net/bkerndev/Docs/title.htm)) you probably
|
||||||
have something like this to handle interrupts:
|
have something like this to handle interrupts:
|
||||||
|
|
||||||
|
:::nasm
|
||||||
int_stub:
|
int_stub:
|
||||||
pusha
|
pusha
|
||||||
|
|
||||||
@ -170,8 +172,10 @@ have something like this to handle interrupts:
|
|||||||
add esp, 8
|
add esp, 8
|
||||||
|
|
||||||
iret
|
iret
|
||||||
{: .lang-nasm}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:::c
|
||||||
void int_handler(registers_t r)
|
void int_handler(registers_t r)
|
||||||
{
|
{
|
||||||
do_stuff();
|
do_stuff();
|
||||||
@ -184,6 +188,7 @@ Anyway. This would take care of both pushing and poping all registers,
|
|||||||
and with only a small modification, it becomes very easy to switch the
|
and with only a small modification, it becomes very easy to switch the
|
||||||
stacks too...
|
stacks too...
|
||||||
|
|
||||||
|
:::nasm
|
||||||
int_stub:
|
int_stub:
|
||||||
pusha
|
pusha
|
||||||
|
|
||||||
@ -212,8 +217,10 @@ stacks too...
|
|||||||
add esp, 8
|
add esp, 8
|
||||||
|
|
||||||
iret
|
iret
|
||||||
{: .lang-nasm }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:::c
|
||||||
registers_t *int_handler(registers_t *r)
|
registers_t *int_handler(registers_t *r)
|
||||||
{
|
{
|
||||||
do_stuff();
|
do_stuff();
|
||||||
@ -249,7 +256,6 @@ kernel space about it.
|
|||||||
+-----------------------+
|
+-----------------------+
|
||||||
|thread information |
|
|thread information |
|
||||||
+-----------------------+
|
+-----------------------+
|
||||||
{: .nopretty}
|
|
||||||
|
|
||||||
Then, when an interrupt or syscall happens, a new stack is loaded
|
Then, when an interrupt or syscall happens, a new stack is loaded
|
||||||
and some stuff is pushed onto it. If we want this near our thread
|
and some stuff is pushed onto it. If we want this near our thread
|
||||||
@ -260,7 +266,6 @@ backwards.
|
|||||||
|thread registers |
|
|thread registers |
|
||||||
|thread information |
|
|thread information |
|
||||||
+-----------------------+
|
+-----------------------+
|
||||||
{: .nopretty}
|
|
||||||
|
|
||||||
Finally, we want the kernel mode stack. Well... the stack pointer is
|
Finally, we want the kernel mode stack. Well... the stack pointer is
|
||||||
right at the start of the registers now, so why not just continue the
|
right at the start of the registers now, so why not just continue the
|
||||||
@ -272,12 +277,12 @@ stack from there?
|
|||||||
|thread registers |
|
|thread registers |
|
||||||
|thread information |
|
|thread information |
|
||||||
+-----------------------+
|
+-----------------------+
|
||||||
{: .nopretty}
|
|
||||||
|
|
||||||
###Setting this up
|
###Setting this up
|
||||||
To set this up, the thread information structure has to be set up
|
To set this up, the thread information structure has to be set up
|
||||||
something like:
|
something like:
|
||||||
|
|
||||||
|
:::c
|
||||||
struct thread_info_struct
|
struct thread_info_struct
|
||||||
{
|
{
|
||||||
uint8_t stack_space[KERNEL_STACK_SIZE];
|
uint8_t stack_space[KERNEL_STACK_SIZE];
|
||||||
@ -289,6 +294,7 @@ When the thread is running in user mode, the TSS should be set up in
|
|||||||
such a way that the stack pointer loaded at an interrupt points to the
|
such a way that the stack pointer loaded at an interrupt points to the
|
||||||
end of the registers, i.e. the beginning of the thread data.
|
end of the registers, i.e. the beginning of the thread data.
|
||||||
|
|
||||||
|
:::c
|
||||||
TSS.esp0 = &my_thread_info.thread_data;
|
TSS.esp0 = &my_thread_info.thread_data;
|
||||||
|
|
||||||
And that's really all there is to it. Unbelievable, really, how many
|
And that's really all there is to it. Unbelievable, really, how many
|
||||||
@ -335,6 +341,7 @@ I recently learned - the hard way - that the [clang
|
|||||||
compiler](http://clang.llvm.org) does not use this calling convention
|
compiler](http://clang.llvm.org) does not use this calling convention
|
||||||
for functions which do not in turn call other functions. I.e
|
for functions which do not in turn call other functions. I.e
|
||||||
|
|
||||||
|
:::c
|
||||||
int double_integer(int a)
|
int double_integer(int a)
|
||||||
{
|
{
|
||||||
return 2*a;
|
return 2*a;
|
||||||
|
@ -21,11 +21,12 @@ what I did, and then I branched off the last commit I blogged about
|
|||||||
|
|
||||||
For future reference (I'll probably cheat again someday) there were the commands for this:
|
For future reference (I'll probably cheat again someday) there were the commands for this:
|
||||||
|
|
||||||
git commit -m 'Bad excuse for not checking in before'
|
:::bash
|
||||||
git checkout -b new_master OLD_COMMIT_SHA
|
$ git commit -m 'Bad excuse for not checking in before'
|
||||||
git merge --strategy=ours master
|
$ git checkout -b new_master OLD_COMMIT_SHA
|
||||||
git checkout master
|
$ git merge --strategy=ours master
|
||||||
git merge new_master
|
$ git checkout master
|
||||||
|
$ git merge new_master
|
||||||
|
|
||||||
The resulting commit is found at
|
The resulting commit is found at
|
||||||
[f74ec287db](https://github.com/thomasloven/os5/tree/f74ec287db488a7bda5
|
[f74ec287db](https://github.com/thomasloven/os5/tree/f74ec287db488a7bda5
|
||||||
|
@ -41,8 +41,8 @@ boasts expressive error messages as a feature...
|
|||||||
|
|
||||||
Anyway with clang version 3.1 you can compile i386-elf object files through
|
Anyway with clang version 3.1 you can compile i386-elf object files through
|
||||||
|
|
||||||
> clang -ccc-host-triple i386-pc-linux -c source.c -o object.o
|
:::bash
|
||||||
{: .prettyprint .lang-sh}
|
$ clang -ccc-host-triple i386-pc-linux -c source.c -o object.o
|
||||||
|
|
||||||
`-ccc-host-triple` isn't mentioned even once in the documentation of
|
`-ccc-host-triple` isn't mentioned even once in the documentation of
|
||||||
clang nor are the possible choices. `i386-elf` which is somewhat of a
|
clang nor are the possible choices. `i386-elf` which is somewhat of a
|
||||||
@ -66,8 +66,8 @@ If you run OSX clang 3.1 is installed with the
|
|||||||
current version of Xcode. Version 3.2 is installed by
|
current version of Xcode. Version 3.2 is installed by
|
||||||
[Homebrew](http://mxcl.github.com/homebrew/).
|
[Homebrew](http://mxcl.github.com/homebrew/).
|
||||||
|
|
||||||
> homebrew install llvm
|
:::bash
|
||||||
{: .prettyprint .lang-sh}
|
$ homebrew install llvm
|
||||||
|
|
||||||
Binutils
|
Binutils
|
||||||
--------
|
--------
|
||||||
@ -82,12 +82,14 @@ Instead, again, I use Homebrew.
|
|||||||
First of all, to get a working cross target binutils, the brew formula
|
First of all, to get a working cross target binutils, the brew formula
|
||||||
will have to be changed a bit
|
will have to be changed a bit
|
||||||
|
|
||||||
> brew edit binutils
|
:::bash
|
||||||
|
$ brew edit binutils
|
||||||
|
|
||||||
Change the last configure flag (` --enable-targets=x86_64-elf,arm-none-eabi,m32r`)
|
Change the last configure flag (` --enable-targets=x86_64-elf,arm-none-eabi,m32r`)
|
||||||
to `--target=i386-elf`. I also changed the `--program-prefix` to `i386-elf-`. Save the file and run
|
to `--target=i386-elf`. I also changed the `--program-prefix` to `i386-elf-`. Save the file and run
|
||||||
|
|
||||||
> brew install binutils
|
:::bash
|
||||||
|
$ brew install binutils
|
||||||
|
|
||||||
and you're good to go.
|
and you're good to go.
|
||||||
|
|
||||||
@ -122,37 +124,42 @@ a text mode (curses mode).
|
|||||||
|
|
||||||
So I went out on a whim and tried
|
So I went out on a whim and tried
|
||||||
|
|
||||||
> brew info qemu
|
:::bash
|
||||||
|
$ brew info qemu
|
||||||
|
|
||||||
By now you should know pretty much what I think of Homebrew, so the
|
By now you should know pretty much what I think of Homebrew, so the
|
||||||
results of that command pretty much sealed the deal.
|
results of that command pretty much sealed the deal.
|
||||||
|
|
||||||
Now I run my kernel in qemu through
|
Now I run my kernel in qemu through
|
||||||
|
|
||||||
> qemu-system-i386 -kernel kernel/kernel -curses
|
:::bash
|
||||||
|
$ qemu-system-i386 -kernel kernel/kernel -curses
|
||||||
|
|
||||||
Qemu also turned out to have a monitor mode which contains some of the
|
Qemu also turned out to have a monitor mode which contains some of the
|
||||||
functions I used most often in the bochs debugger, such as printing the
|
functions I used most often in the bochs debugger, such as printing the
|
||||||
memory map. Further, this could be accessed using telnet from a
|
memory map. Further, this could be accessed using telnet from a
|
||||||
different tmux pane.
|
different tmux pane.
|
||||||
|
|
||||||
|
:::bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
tmux split-window -h 'qemu-system-i386 -kernel kernel/kernel -curses -monitor telnet:localhost:4444,server'
|
tmux split-window -h 'qemu-system-i386 -kernel kernel/kernel -curses -monitor telnet:localhost:4444,server'
|
||||||
tmux select-pane -L
|
tmux select-pane -L
|
||||||
telnet localhost 4444
|
telnet localhost 4444
|
||||||
{: .prettyprint}
|
|
||||||
|
|
||||||
Finally, I also installed an i386-elf targeted version of gdb - using Homebrew, obviously, with the same trick as for binutils. Gdb is found in a different tap of homebrew, so that will have to be installed first
|
Finally, I also installed an i386-elf targeted version of gdb - using Homebrew, obviously, with the same trick as for binutils. Gdb is found in a different tap of homebrew, so that will have to be installed first
|
||||||
|
|
||||||
> brew tap homebrew/dupes
|
:::bash
|
||||||
> brew edit gdb
|
$ brew tap homebrew/dupes
|
||||||
|
$ brew edit gdb
|
||||||
|
|
||||||
Add the flag `--target=i386-elf` to the configure flags, save and
|
Add the flag `--target=i386-elf` to the configure flags, save and
|
||||||
|
|
||||||
> brew install gdb
|
:::bash
|
||||||
|
$ brew install gdb
|
||||||
|
|
||||||
This will link to `ì386-elf-gdb` and can be run in yet another tmux window.
|
This will link to `ì386-elf-gdb` and can be run in yet another tmux window.
|
||||||
|
|
||||||
|
:::bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
tmux split-window -h 'qemu-system-i386 -kernel kernel/kernel -curses -monitor telnet:localhost:4444,server -s -S'
|
tmux split-window -h 'qemu-system-i386 -kernel kernel/kernel -curses -monitor telnet:localhost:4444,server -s -S'
|
||||||
tmux select-pane -L
|
tmux select-pane -L
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
layout: post
|
layout: post
|
||||||
title: "Two Small Projects"
|
title: "Two Small Projects"
|
||||||
subtitle: "Audio and Arduino"
|
subtitle: "Audio and Arduino"
|
||||||
|
tags: [electronics]
|
||||||
|
|
||||||
###Breadboard Arduino
|
###Breadboard Arduino
|
||||||
I didn't really like the arduino when I first heard about it. It
|
I didn't really like the arduino when I first heard about it. It
|
||||||
|
@ -16,6 +16,7 @@ should not affect the other.
|
|||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
|
:::c
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
@ -43,7 +44,6 @@ For example:
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
{: .lang-c}
|
|
||||||
|
|
||||||
This simple program should output (assuming the parent is run first and
|
This simple program should output (assuming the parent is run first and
|
||||||
is not interrupted):
|
is not interrupted):
|
||||||
@ -93,6 +93,7 @@ Finally, each area has a pointer to its owning process.
|
|||||||
Let's follow a memory area during part of a process' life.
|
Let's follow a memory area during part of a process' life.
|
||||||
|
|
||||||
###Setup
|
###Setup
|
||||||
|
|
||||||
{: .center .noborder}
|
{: .center .noborder}
|
||||||
|
|
||||||
In the figure above we see two processes, _A_ and _B_.
|
In the figure above we see two processes, _A_ and _B_.
|
||||||
@ -112,13 +113,15 @@ In other words, it is two memory pages long (assuming 4kb pages).
|
|||||||
|
|
||||||
The user types
|
The user types
|
||||||
|
|
||||||
> gcc hello_world.c
|
:::bash
|
||||||
|
$ gcc hello_world.c
|
||||||
|
|
||||||
into the terminal and the shell program executes the `fork` system call.
|
into the terminal and the shell program executes the `fork` system call.
|
||||||
|
|
||||||
This makes the kernel do a lot of things, one of which is create a new
|
This makes the kernel do a lot of things, one of which is create a new
|
||||||
memory map for the new process. It then clones all memory areas into the
|
memory map for the new process. It then clones all memory areas into the
|
||||||
new map.
|
new map.
|
||||||
|
|
||||||
{: .center .noborder}
|
{: .center .noborder}
|
||||||
|
|
||||||
The write flag of our area is unset and the CoW flag is set. The area is
|
The write flag of our area is unset and the CoW flag is set. The area is
|
||||||
@ -149,6 +152,7 @@ A while later, the parent process is scheduled in and it may also wish
|
|||||||
to write to the stack. This time the area is already split in two, and
|
to write to the stack. This time the area is already split in two, and
|
||||||
the required area has no copies, so it is just set as read/write and
|
the required area has no copies, so it is just set as read/write and
|
||||||
we're done.
|
we're done.
|
||||||
|
|
||||||
{: .center .noborder}
|
{: .center .noborder}
|
||||||
|
|
||||||
Actually, the parent process will probably perform a `waitpid` syscall
|
Actually, the parent process will probably perform a `waitpid` syscall
|
||||||
|
@ -6,6 +6,7 @@ tags: [osdev]
|
|||||||
System calls is the way user processes communicate to the kernel. Look
|
System calls is the way user processes communicate to the kernel. Look
|
||||||
at the following program, for example.
|
at the following program, for example.
|
||||||
|
|
||||||
|
:::c
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
@ -14,7 +15,6 @@ at the following program, for example.
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
{: .lang-c}
|
|
||||||
|
|
||||||
When you call the program, even before it is started, the shell makes a
|
When you call the program, even before it is started, the shell makes a
|
||||||
couple of system calls such as `fork()` and `exec()`. The program itself
|
couple of system calls such as `fork()` and `exec()`. The program itself
|
||||||
@ -37,6 +37,7 @@ implemented it in my kernel yet, but here's how it would work.
|
|||||||
####User side
|
####User side
|
||||||
First the definition in the c library:
|
First the definition in the c library:
|
||||||
|
|
||||||
|
:::c
|
||||||
int read(int file, char *ptr, int len)
|
int read(int file, char *ptr, int len)
|
||||||
{
|
{
|
||||||
return _syscall_read(file, ptr, len);
|
return _syscall_read(file, ptr, len);
|
||||||
@ -44,13 +45,13 @@ First the definition in the c library:
|
|||||||
|
|
||||||
Simply a wrapper for an assembly function:
|
Simply a wrapper for an assembly function:
|
||||||
|
|
||||||
|
:::nasm
|
||||||
[global _syscall_read]
|
[global _syscall_read]
|
||||||
_syscall_read:
|
_syscall_read:
|
||||||
mov eax, SYSCALL_READ
|
mov eax, SYSCALL_READ
|
||||||
int 0x80
|
int 0x80
|
||||||
mov [syscall_error], edx
|
mov [syscall_error], edx
|
||||||
ret
|
ret
|
||||||
{: .lang-nasm}
|
|
||||||
|
|
||||||
This function puts an identifier for the system call in the `eax`
|
This function puts an identifier for the system call in the `eax`
|
||||||
register and then execute the system call interrupt.
|
register and then execute the system call interrupt.
|
||||||
@ -62,14 +63,15 @@ Conventions](http://wiki.osdev.org/Calling_Conventions) more carefully.
|
|||||||
|
|
||||||
Of course, this can be simplified with a macro to
|
Of course, this can be simplified with a macro to
|
||||||
|
|
||||||
|
:::nasm
|
||||||
[global _syscall_read]
|
[global _syscall_read]
|
||||||
DEF_SYSCALL(read, SYSCALL_READ)
|
DEF_SYSCALL(read, SYSCALL_READ)
|
||||||
{: .lang-nasm}
|
|
||||||
|
|
||||||
####Kernel side
|
####Kernel side
|
||||||
|
|
||||||
In the kernel, the system call is caught by the following function:
|
In the kernel, the system call is caught by the following function:
|
||||||
|
|
||||||
|
:::c
|
||||||
registers_t *syscall_handler(registers_t *r)
|
registers_t *syscall_handler(registers_t *r)
|
||||||
{
|
{
|
||||||
if(syscall_handlers[r->eax])
|
if(syscall_handlers[r->eax])
|
||||||
@ -84,6 +86,7 @@ If the system call is registered correctly in the kernel (through the
|
|||||||
macro `KREG_SYSCALL(read, SYSCALL_READ)`), this will pass everything
|
macro `KREG_SYSCALL(read, SYSCALL_READ)`), this will pass everything
|
||||||
onto the following function:
|
onto the following function:
|
||||||
|
|
||||||
|
:::c
|
||||||
KDEF_SYSCALL(read, r)
|
KDEF_SYSCALL(read, r)
|
||||||
{
|
{
|
||||||
process_stack stack = init_pstack();
|
process_stack stack = init_pstack();
|
||||||
@ -100,6 +103,7 @@ they are pushed on call.
|
|||||||
|
|
||||||
Then the `read()` function has the same definition as the library version.
|
Then the `read()` function has the same definition as the library version.
|
||||||
|
|
||||||
|
:::c
|
||||||
int read(int file, char *ptr, int len)
|
int read(int file, char *ptr, int len)
|
||||||
{
|
{
|
||||||
...
|
...
|
||||||
|
@ -40,6 +40,7 @@ compiling newlib with that setup is rather annoying.
|
|||||||
|
|
||||||
I'm also a fan of clean makefiles. Take a look at this:
|
I'm also a fan of clean makefiles. Take a look at this:
|
||||||
|
|
||||||
|
:::make
|
||||||
VPATH := ../src
|
VPATH := ../src
|
||||||
|
|
||||||
CC := i586-pc-myos-gcc
|
CC := i586-pc-myos-gcc
|
||||||
@ -52,7 +53,6 @@ I'm also a fan of clean makefiles. Take a look at this:
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
-rm $(TARGETS) 2>/dev/null
|
-rm $(TARGETS) 2>/dev/null
|
||||||
{: .lang-make}
|
|
||||||
|
|
||||||
That's the makefile for the entire `/bin` directory in my os.
|
That's the makefile for the entire `/bin` directory in my os.
|
||||||
|
|
||||||
@ -67,13 +67,14 @@ make a formula for it. So after applying the patches described in the
|
|||||||
post (I even kept the name `i586-pc-myos` since I don't have a working
|
post (I even kept the name `i586-pc-myos` since I don't have a working
|
||||||
name for my kernel besides an iteration number...) I did
|
name for my kernel besides an iteration number...) I did
|
||||||
|
|
||||||
export TARGET=i586-pc-myos
|
:::bash
|
||||||
export PREFIX=/usr/local/Cellar/osdev/1.0
|
$ export TARGET=i586-pc-myos
|
||||||
|
$ export PREFIX=/usr/local/Cellar/osdev/1.0
|
||||||
# Configure, build and install binutils
|
# Configure, build and install binutils
|
||||||
brew link osdev
|
$ brew link osdev
|
||||||
# Configure, build and install gcc and libgcc
|
# Configure, build and install gcc and libgcc
|
||||||
brew unlink osdev
|
$ brew unlink osdev
|
||||||
brew link osdev
|
$ brew link osdev
|
||||||
|
|
||||||
And that prepared me for building newlib.
|
And that prepared me for building newlib.
|
||||||
|
|
||||||
@ -85,24 +86,24 @@ automake and autoconf since a couple of versions. More specifically,
|
|||||||
you need automake version 1.12 or earlier and autoconf version 2.64.
|
you need automake version 1.12 or earlier and autoconf version 2.64.
|
||||||
Unfortunately, those versions are not available through Homebrew, so ...
|
Unfortunately, those versions are not available through Homebrew, so ...
|
||||||
|
|
||||||
curl -O http://ftp.gnu.org/gnu/automake/automake-1.12.tar.gz
|
:::bash
|
||||||
tar -zxf automake-1.12.tar.gz
|
$ curl -O http://ftp.gnu.org/gnu/automake/automake-1.12.tar.gz
|
||||||
mkdir -p build-automake
|
$ tar -zxf automake-1.12.tar.gz
|
||||||
pushd build-automake
|
$ mkdir -p build-automake
|
||||||
../automake-1.12/configure --prefix=/usr/local/Cellar/automake/1.12
|
$ pushd build-automake
|
||||||
make all -j
|
$ ../automake-1.12/configure --prefix=/usr/local/Cellar/automake/1.12
|
||||||
make install
|
$ make all -j
|
||||||
popd
|
$ make install
|
||||||
curl -O http://ftp.gnu.org/gnu/autoconf/autoconf-2.64.tar.gz
|
$ popd
|
||||||
tar -zxf autoconf-2.64.tar.gz
|
$ curl -O http://ftp.gnu.org/gnu/autoconf/autoconf-2.64.tar.gz
|
||||||
pushd build-autoconf
|
$ tar -zxf autoconf-2.64.tar.gz
|
||||||
../autoconf-2.64/configure --prefix=/usr/local/Cellar/autoconf/2.64
|
$ pushd build-autoconf
|
||||||
make all -j
|
$ ../autoconf-2.64/configure --prefix=/usr/local/Cellar/autoconf/2.64
|
||||||
make install
|
$ make all -j
|
||||||
popd
|
$ make install
|
||||||
|
$ popd
|
||||||
brew switch automake 1.12
|
$ brew switch automake 1.12
|
||||||
brew switch autoconf 2.64
|
$ brew switch autoconf 2.64
|
||||||
|
|
||||||
Those last two lines tells Homebrew that you want to use those specific
|
Those last two lines tells Homebrew that you want to use those specific
|
||||||
versions for now.
|
versions for now.
|
||||||
@ -111,24 +112,26 @@ Now for the neat part. I followed the wiki post and used the
|
|||||||
[syscall interface](/blog/2013/06/System-Calls) i've described earlier
|
[syscall interface](/blog/2013/06/System-Calls) i've described earlier
|
||||||
but I also wrapped `crt0.S` and `syscalls.c` in
|
but I also wrapped `crt0.S` and `syscalls.c` in
|
||||||
|
|
||||||
|
:::c
|
||||||
#ifndef KERNEL_MODE
|
#ifndef KERNEL_MODE
|
||||||
...
|
...
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Then I built it all through
|
Then I built it all through
|
||||||
|
|
||||||
pushd build-newlib
|
:::bash
|
||||||
../newlib/configure --target=$TARGET --prefix=$PREFIX
|
$ pushd build-newlib
|
||||||
export CPPFLAGS_FOR_TARGET=-DKERNEL_MODE
|
$ ../newlib/configure --target=$TARGET --prefix=$PREFIX
|
||||||
make -j
|
$ export CPPFLAGS_FOR_TARGET=-DKERNEL_MODE
|
||||||
make install
|
$ make -j
|
||||||
mv $PREFIX/$TARGET/lib/libc.a $PREFIX/$TARGET/lib/libkernel.a
|
$ make install
|
||||||
rm -rf *
|
$ mv $PREFIX/$TARGET/lib/libc.a $PREFIX/$TARGET/lib/libkernel.a
|
||||||
../newlib/configure --target=$TARGET --prefix=$PREFIX
|
$ rm -rf *
|
||||||
export CPPFLAGS_FOR_TARGET=
|
$ ../newlib/configure --target=$TARGET --prefix=$PREFIX
|
||||||
make -j
|
$ export CPPFLAGS_FOR_TARGET=
|
||||||
make install
|
$ make -j
|
||||||
popd
|
$ make install
|
||||||
|
$ popd
|
||||||
|
|
||||||
This gives me two versions of the newlib c library. One with all syscalls
|
This gives me two versions of the newlib c library. One with all syscalls
|
||||||
defined and one without. The latter is suitable for linking into the
|
defined and one without. The latter is suitable for linking into the
|
||||||
@ -160,6 +163,7 @@ compile the kernel using a very simple makefile.
|
|||||||
|
|
||||||
Somewhat simplified:
|
Somewhat simplified:
|
||||||
|
|
||||||
|
:::make
|
||||||
OBJECTS := $(shell find . -name "*.S")
|
OBJECTS := $(shell find . -name "*.S")
|
||||||
OBJECTS += $(shell find . -name "*.c")
|
OBJECTS += $(shell find . -name "*.c")
|
||||||
OBJECTS := $(OBJECTS:%.S=%.o)
|
OBJECTS := $(OBJECTS:%.S=%.o)
|
||||||
@ -177,6 +181,7 @@ including preprocessing and assembling .S files.
|
|||||||
|
|
||||||
For executables running under my operating system it's even easier
|
For executables running under my operating system it's even easier
|
||||||
|
|
||||||
|
:::make
|
||||||
CC := i586-pc-myos-gcc
|
CC := i586-pc-myos-gcc
|
||||||
|
|
||||||
That's all.
|
That's all.
|
||||||
|
@ -10,6 +10,7 @@ where to find everything. It has the following structure. The
|
|||||||
[ELF specification](http://www.skyfree.org/linux/references/ELF_Format.pdf)
|
[ELF specification](http://www.skyfree.org/linux/references/ELF_Format.pdf)
|
||||||
gives an excellent description on the meaning and use of each field.
|
gives an excellent description on the meaning and use of each field.
|
||||||
|
|
||||||
|
:::c
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint8_t identity[16];
|
uint8_t identity[16];
|
||||||
@ -38,6 +39,7 @@ identity field. The first four bytes of this filed should always be
|
|||||||
`0x7F`,`'E'`,`'L'`,`'F'`. If that's correct, we can look at the `type`
|
`0x7F`,`'E'`,`'L'`,`'F'`. If that's correct, we can look at the `type`
|
||||||
field. For an executable standalone program, this should be `2`.
|
field. For an executable standalone program, this should be `2`.
|
||||||
|
|
||||||
|
:::c
|
||||||
int load_elf(uint8_t *data)
|
int load_elf(uint8_t *data)
|
||||||
{
|
{
|
||||||
elf_header *elf = (elf_header *)data;
|
elf_header *elf = (elf_header *)data;
|
||||||
@ -48,6 +50,7 @@ field. For an executable standalone program, this should be `2`.
|
|||||||
`is_elf` looks as follows. Note the use of `strncmp` which I can do
|
`is_elf` looks as follows. Note the use of `strncmp` which I can do
|
||||||
because I link [newlib into my kernel](/blog/2013/08/Catching-Up/).
|
because I link [newlib into my kernel](/blog/2013/08/Catching-Up/).
|
||||||
|
|
||||||
|
:::c
|
||||||
int is_elf(elf_header *elf)
|
int is_elf(elf_header *elf)
|
||||||
{
|
{
|
||||||
int iself = -1;
|
int iself = -1;
|
||||||
@ -70,6 +73,7 @@ For just loading a simple ELF program, we only need to look at the
|
|||||||
program headers which are located in a table at offset `ph_offset` in
|
program headers which are located in a table at offset `ph_offset` in
|
||||||
the file.
|
the file.
|
||||||
|
|
||||||
|
:::c
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
@ -87,6 +91,7 @@ use them to find out what parts of the elf image should be loaded where
|
|||||||
in memory. So, the next step would be to go through all program headers
|
in memory. So, the next step would be to go through all program headers
|
||||||
looking for loadable sections and load them into memory.
|
looking for loadable sections and load them into memory.
|
||||||
|
|
||||||
|
:::c
|
||||||
...
|
...
|
||||||
elf_phead *phead = (elf_phead)&data[elf->ph_offset];
|
elf_phead *phead = (elf_phead)&data[elf->ph_offset];
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
@ -106,6 +111,7 @@ of code and data for example.
|
|||||||
|
|
||||||
Anyway, `load_elf_segment()` looks like this
|
Anyway, `load_elf_segment()` looks like this
|
||||||
|
|
||||||
|
:::c
|
||||||
void load_elf_segment(uint8_t *data, elf_phead *phead)
|
void load_elf_segment(uint8_t *data, elf_phead *phead)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ What? But my code didn't even run!
|
|||||||
I reapplied my changes one at a time and found the line that caused the
|
I reapplied my changes one at a time and found the line that caused the
|
||||||
problem.
|
problem.
|
||||||
|
|
||||||
|
:::c
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
What?? That's it? Declaring a variable?
|
What?? That's it? Declaring a variable?
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
layout: post
|
layout: post
|
||||||
title: "Virtual File System 2"
|
title: "Virtual File System 2"
|
||||||
subtitle: "for real this time."
|
subtitle: "for real this time."
|
||||||
tags: [osdev]
|
tags: [osdev, filesystems]
|
||||||
|
|
||||||
Once again, several months have passed since I wrote anything here.
|
Once again, several months have passed since I wrote anything here.
|
||||||
I also worked very little on the kernel during this time, though. So no
|
I also worked very little on the kernel during this time, though. So no
|
||||||
@ -40,6 +40,7 @@ system in mind.
|
|||||||
|
|
||||||
The VFS should offer the functions
|
The VFS should offer the functions
|
||||||
|
|
||||||
|
:::c
|
||||||
open() // Open or create a file
|
open() // Open or create a file
|
||||||
close() // Close a file
|
close() // Close a file
|
||||||
read() // Read data from opened file
|
read() // Read data from opened file
|
||||||
@ -96,7 +97,7 @@ Example:
|
|||||||
A good starting point for the inode structure might be some pointers to
|
A good starting point for the inode structure might be some pointers to
|
||||||
allow it to be placed in a tree, then.
|
allow it to be placed in a tree, then.
|
||||||
|
|
||||||
|
:::c
|
||||||
struct vfs_node_st;
|
struct vfs_node_st;
|
||||||
typedef vfs_node_t * INODE;
|
typedef vfs_node_t * INODE;
|
||||||
|
|
||||||
@ -109,8 +110,6 @@ allow it to be placed in a tree, then.
|
|||||||
uint32_t type;
|
uint32_t type;
|
||||||
} vfs_node_t;
|
} vfs_node_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
This does waste a bit of memory, since most inodes that are used by the
|
This does waste a bit of memory, since most inodes that are used by the
|
||||||
system won't be in the VFS tree, but four `size_t` isn't that much,
|
system won't be in the VFS tree, but four `size_t` isn't that much,
|
||||||
and it's far from the worst memory thief in this kernel anyway.
|
and it's far from the worst memory thief in this kernel anyway.
|
||||||
@ -125,6 +124,7 @@ to maintain.
|
|||||||
Then, we need a way to keep track of the driver, i.e. the functions
|
Then, we need a way to keep track of the driver, i.e. the functions
|
||||||
called to access the file. To do this, I define a new struct:
|
called to access the file. To do this, I define a new struct:
|
||||||
|
|
||||||
|
:::c
|
||||||
typedef struct vfs_driver_st
|
typedef struct vfs_driver_st
|
||||||
{
|
{
|
||||||
uint32_t (*open)(INODE, uint32_t);
|
uint32_t (*open)(INODE, uint32_t);
|
||||||
@ -145,6 +145,7 @@ value, a void pointer for arbitrary data used by the drivers and a flags
|
|||||||
value - also for use by the drivers. The
|
value - also for use by the drivers. The
|
||||||
inode struct now looks like this:
|
inode struct now looks like this:
|
||||||
|
|
||||||
|
:::c
|
||||||
typedef struct vfs_node_st
|
typedef struct vfs_node_st
|
||||||
{
|
{
|
||||||
char name[VFS_NAME_SZ];
|
char name[VFS_NAME_SZ];
|
||||||
@ -161,6 +162,7 @@ inode struct now looks like this:
|
|||||||
###Vfs functions
|
###Vfs functions
|
||||||
Next, I create some wrapper functions to call the driver functions.
|
Next, I create some wrapper functions to call the driver functions.
|
||||||
|
|
||||||
|
:::c
|
||||||
uint32_t vfs_open(INODE ino, uint32_t mode)
|
uint32_t vfs_open(INODE ino, uint32_t mode)
|
||||||
{
|
{
|
||||||
if(ino->d->open)
|
if(ino->d->open)
|
||||||
@ -172,6 +174,7 @@ and similar for all functions except `readdir` and `finddir` which
|
|||||||
contain code to handle `.` and `..` for mount roots.
|
contain code to handle `.` and `..` for mount roots.
|
||||||
|
|
||||||
|
|
||||||
|
:::c
|
||||||
dirent_t *vfs_readdir(INODE ino, uint32_t num)
|
dirent_t *vfs_readdir(INODE ino, uint32_t num)
|
||||||
{
|
{
|
||||||
if(ino->type & FS_MOUNT)
|
if(ino->type & FS_MOUNT)
|
||||||
@ -196,6 +199,7 @@ contain code to handle `.` and `..` for mount roots.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:::c
|
||||||
INODE vfs_finddir(INODE ino, const char *name)
|
INODE vfs_finddir(INODE ino, const char *name)
|
||||||
{
|
{
|
||||||
if(ino->type & FS_MOUNT)
|
if(ino->type & FS_MOUNT)
|
||||||
@ -241,6 +245,7 @@ _Warning:_ Pointer-pointers ahead!
|
|||||||
|
|
||||||
First: a function for traversing the mount tree as far as possible
|
First: a function for traversing the mount tree as far as possible
|
||||||
|
|
||||||
|
:::c
|
||||||
INODE vfs_find_root(char **path)
|
INODE vfs_find_root(char **path)
|
||||||
{
|
{
|
||||||
// Find closest point in mount tree
|
// Find closest point in mount tree
|
||||||
@ -284,6 +289,7 @@ The namei/mount function then uses this as a starting point:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:::c
|
||||||
INODE vfs_namei_mount(const char *path, INODE root)
|
INODE vfs_namei_mount(const char *path, INODE root)
|
||||||
{
|
{
|
||||||
char *npath = strdup(path);
|
char *npath = strdup(path);
|
||||||
@ -346,7 +352,6 @@ The namei/mount function then uses this as a starting point:
|
|||||||
}
|
}
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
{: .lang-c}
|
|
||||||
|
|
||||||
Note how `pth` is changed by `vfs_find_root()` to only contain the part
|
Note how `pth` is changed by `vfs_find_root()` to only contain the part
|
||||||
of the path that wasn't found. After that, we ask each node for the next
|
of the path that wasn't found. After that, we ask each node for the next
|
||||||
@ -360,6 +365,7 @@ the final inode is returned.
|
|||||||
I also made two simple wrappers for this function:
|
I also made two simple wrappers for this function:
|
||||||
|
|
||||||
|
|
||||||
|
:::c
|
||||||
INODE vfs_namei(const char *path)
|
INODE vfs_namei(const char *path)
|
||||||
{
|
{
|
||||||
return vfs_namei_mount(path, 0);
|
return vfs_namei_mount(path, 0);
|
||||||
@ -369,11 +375,11 @@ I also made two simple wrappers for this function:
|
|||||||
{
|
{
|
||||||
return vfs_namei_mount(path, root);
|
return vfs_namei_mount(path, root);
|
||||||
}
|
}
|
||||||
{: .lang-c}
|
|
||||||
|
|
||||||
And finally, a function for unmounting file systems:
|
And finally, a function for unmounting file systems:
|
||||||
|
|
||||||
|
|
||||||
|
:::c
|
||||||
INODE vfs_umount(const char *path)
|
INODE vfs_umount(const char *path)
|
||||||
{
|
{
|
||||||
char *npath = strdup(path);
|
char *npath = strdup(path);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
layout: post
|
layout: post
|
||||||
title: "The debug file system"
|
title: "The debug file system"
|
||||||
subtitle: "It's still in the kernel!"
|
subtitle: "It's still in the kernel!"
|
||||||
tags: [osdev]
|
tags: [osdev, filesystems]
|
||||||
|
|
||||||
This far in my vfs rewrite, I've made a [vfs mount tree](/blog/2013/12/Virtual-File-System2/) and written some [file operation syscalls](/blog/2013/12/VFS-syscalls/). Now it's time to actually use this, by writing a filesystem that can be mounted and then manipulated through the syscall interface.
|
This far in my vfs rewrite, I've made a [vfs mount tree](/blog/2013/12/Virtual-File-System2/) and written some [file operation syscalls](/blog/2013/12/VFS-syscalls/). Now it's time to actually use this, by writing a filesystem that can be mounted and then manipulated through the syscall interface.
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ Three functions are needed for newlib `fprintf` to be able to write to
|
|||||||
it: `stat`, `isatty` and `write`. Those are all rather simple, since
|
it: `stat`, `isatty` and `write`. Those are all rather simple, since
|
||||||
they won't need to keep track of which file we're referencing.
|
they won't need to keep track of which file we're referencing.
|
||||||
|
|
||||||
|
:::c
|
||||||
uint32_t debug_stat(INODE node, struct stat *st)
|
uint32_t debug_stat(INODE node, struct stat *st)
|
||||||
{
|
{
|
||||||
memset(st, 0, sizeof(struct stat));
|
memset(st, 0, sizeof(struct stat));
|
||||||
@ -25,6 +26,7 @@ they won't need to keep track of which file we're referencing.
|
|||||||
I don't care much about the stat for the debug file. Maybe I'll add some
|
I don't care much about the stat for the debug file. Maybe I'll add some
|
||||||
creation time or so later...
|
creation time or so later...
|
||||||
|
|
||||||
|
:::c
|
||||||
uint32_t debug_isatty(INODE node)
|
uint32_t debug_isatty(INODE node)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
@ -32,6 +34,7 @@ creation time or so later...
|
|||||||
|
|
||||||
The debug output is a terminal.
|
The debug output is a terminal.
|
||||||
|
|
||||||
|
:::c
|
||||||
uint32_t debug_write(INODE node, void *buffer, uint32_t size, uint32_t offset)
|
uint32_t debug_write(INODE node, void *buffer, uint32_t size, uint32_t offset)
|
||||||
{
|
{
|
||||||
char *buf = calloc(size + 1, 1);
|
char *buf = calloc(size + 1, 1);
|
||||||
@ -48,6 +51,7 @@ data is null-terminated.
|
|||||||
|
|
||||||
With this, I can define a driver for the "debug device":
|
With this, I can define a driver for the "debug device":
|
||||||
|
|
||||||
|
:::c
|
||||||
vfs_driver_t debug_driver =
|
vfs_driver_t debug_driver =
|
||||||
{
|
{
|
||||||
0, // open
|
0, // open
|
||||||
@ -65,6 +69,7 @@ With this, I can define a driver for the "debug device":
|
|||||||
|
|
||||||
And then write a function to setup the device:
|
And then write a function to setup the device:
|
||||||
|
|
||||||
|
:::c
|
||||||
INODE debug_dev_init()
|
INODE debug_dev_init()
|
||||||
{
|
{
|
||||||
INODE node = calloc(1, sizeof(vfs_node_t));
|
INODE node = calloc(1, sizeof(vfs_node_t));
|
||||||
@ -76,17 +81,20 @@ And then write a function to setup the device:
|
|||||||
|
|
||||||
Then, to activate it, all I need to do is add
|
Then, to activate it, all I need to do is add
|
||||||
|
|
||||||
|
:::c
|
||||||
vfs_init();
|
vfs_init();
|
||||||
vfs_mount("/", debug_dev_init());
|
vfs_mount("/", debug_dev_init());
|
||||||
|
|
||||||
in my kernel boot code. After that I can use the standard library
|
in my kernel boot code. After that I can use the standard library
|
||||||
functions:
|
functions:
|
||||||
|
|
||||||
|
:::c
|
||||||
FILE *dbg = fopen("/", "w");
|
FILE *dbg = fopen("/", "w");
|
||||||
fprintf(dbg, "Hello, world!\n");
|
fprintf(dbg, "Hello, world!\n");
|
||||||
|
|
||||||
or even:
|
or even:
|
||||||
|
|
||||||
|
:::c
|
||||||
fopen("/", "r");
|
fopen("/", "r");
|
||||||
fopen("/", "w");
|
fopen("/", "w");
|
||||||
printf("Hello, world!\n");
|
printf("Hello, world!\n");
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
layout: post
|
layout: post
|
||||||
title: "Pipes"
|
title: "Pipes"
|
||||||
subtitle: "... and keyboard."
|
subtitle: "... and keyboard."
|
||||||
tags: [osdev]
|
tags: [osdev, filesystems]
|
||||||
|
|
||||||
In most unix-like systems, pipes can be used to make processes
|
In most unix-like systems, pipes can be used to make processes
|
||||||
communicate with each other. To the processes the pipe looks just like
|
communicate with each other. To the processes the pipe looks just like
|
||||||
@ -33,6 +33,7 @@ Consumer/Reader:
|
|||||||
|
|
||||||
In code this translates to
|
In code this translates to
|
||||||
|
|
||||||
|
:::c
|
||||||
uint32_t pipe_write(INODE ino, void *buffer, uint32_t size, uint32_t offset)
|
uint32_t pipe_write(INODE ino, void *buffer, uint32_t size, uint32_t offset)
|
||||||
{
|
{
|
||||||
vfs_pipe_t *pipe = (vfs_pipe_t *)ino->data;
|
vfs_pipe_t *pipe = (vfs_pipe_t *)ino->data;
|
||||||
@ -95,6 +96,7 @@ two separate inodes - one that can be written to and one that can be
|
|||||||
read from. The `data` field of the `vfs_node_st` struct of the two
|
read from. The `data` field of the `vfs_node_st` struct of the two
|
||||||
inodes point to the same pipe struct.
|
inodes point to the same pipe struct.
|
||||||
|
|
||||||
|
:::c
|
||||||
typedef struct vfs_pipe
|
typedef struct vfs_pipe
|
||||||
{
|
{
|
||||||
char *buffer;
|
char *buffer;
|
||||||
@ -112,6 +114,7 @@ write end respectively are opened or closed... respectively...
|
|||||||
|
|
||||||
Creating a new pipe (somewhat simplified):
|
Creating a new pipe (somewhat simplified):
|
||||||
|
|
||||||
|
:::c
|
||||||
uint32_t new_pipe(uint32_t size, INODE *nodes)
|
uint32_t new_pipe(uint32_t size, INODE *nodes)
|
||||||
{
|
{
|
||||||
vfs_pipe_t *pipe = calloc(1, sizeof(vfs_pipe_t));
|
vfs_pipe_t *pipe = calloc(1, sizeof(vfs_pipe_t));
|
||||||
@ -136,6 +139,7 @@ Creating a new pipe (somewhat simplified):
|
|||||||
When starting up the keyboard driver, a pipe is created and connected to
|
When starting up the keyboard driver, a pipe is created and connected to
|
||||||
`stdin` - the first file descriptor - of the current process.
|
`stdin` - the first file descriptor - of the current process.
|
||||||
|
|
||||||
|
:::c
|
||||||
void keyboard_init()
|
void keyboard_init()
|
||||||
{
|
{
|
||||||
INODE tmp[2];
|
INODE tmp[2];
|
||||||
@ -158,6 +162,7 @@ The keyboard handler (based off
|
|||||||
[Brandon Friesen's tutorial](http://www.osdever.net/bkerndev/Docs/keyboard.htm)
|
[Brandon Friesen's tutorial](http://www.osdever.net/bkerndev/Docs/keyboard.htm)
|
||||||
writes each decoded key to the pipe:
|
writes each decoded key to the pipe:
|
||||||
|
|
||||||
|
:::c
|
||||||
registers_t *keyboard_handler(registers_t *r)
|
registers_t *keyboard_handler(registers_t *r)
|
||||||
{
|
{
|
||||||
char code[2];
|
char code[2];
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
layout: post
|
layout: post
|
||||||
title: "TAR Filesystem"
|
title: "TAR Filesystem"
|
||||||
subtitle: "Almost a useful system"
|
subtitle: "Almost a useful system"
|
||||||
tags: [osdev]
|
tags: [osdev, filesystems]
|
||||||
|
|
||||||
It's finally time to implement an actuall filesystem, and all the hard
|
It's finally time to implement an actuall filesystem, and all the hard
|
||||||
work with the VFS framework will pay off.
|
work with the VFS framework will pay off.
|
||||||
@ -19,6 +19,7 @@ is preceded by a data block in human-readable format.
|
|||||||
|
|
||||||
In the POSIX standard of 1988, the data block has the following format:
|
In the POSIX standard of 1988, the data block has the following format:
|
||||||
|
|
||||||
|
:::c
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
unsigned char name[100];
|
unsigned char name[100];
|
||||||
@ -56,6 +57,7 @@ are loaded into ram and you might want to access them randomly.
|
|||||||
|
|
||||||
Instead, I reshape the file list into a file tree:
|
Instead, I reshape the file list into a file tree:
|
||||||
|
|
||||||
|
:::c
|
||||||
tree_t *build_tar_tree(tar_header_t *tar)
|
tree_t *build_tar_tree(tar_header_t *tar)
|
||||||
{
|
{
|
||||||
...
|
...
|
||||||
@ -112,6 +114,7 @@ strange.
|
|||||||
The tarfs driver makes use of the data field in the vfs node to store
|
The tarfs driver makes use of the data field in the vfs node to store
|
||||||
the tar tree.
|
the tar tree.
|
||||||
|
|
||||||
|
:::c
|
||||||
INODE tarfs_init(tar_header_t *tar)
|
INODE tarfs_init(tar_header_t *tar)
|
||||||
{
|
{
|
||||||
vfs_node_t *node = calloc(1, sizeof(vfs_node_t));
|
vfs_node_t *node = calloc(1, sizeof(vfs_node_t));
|
||||||
@ -128,6 +131,7 @@ the tar tree.
|
|||||||
|
|
||||||
I then add the following to my kernel initialization function:
|
I then add the following to my kernel initialization function:
|
||||||
|
|
||||||
|
:::c
|
||||||
tar_header_t *tarfs_location = assert_higher((tar_header_t *)mods[0].mod_start);
|
tar_header_t *tarfs_location = assert_higher((tar_header_t *)mods[0].mod_start);
|
||||||
vfs_mount("/", tarfs_init(tarfs_location));
|
vfs_mount("/", tarfs_init(tarfs_location));
|
||||||
|
|
||||||
@ -137,6 +141,7 @@ where `mods` is the multiboot modules table as loaded by grub.
|
|||||||
Finally, the tarfs driver functions. For now I only need to implement
|
Finally, the tarfs driver functions. For now I only need to implement
|
||||||
`read()` and `finddir()`.
|
`read()` and `finddir()`.
|
||||||
|
|
||||||
|
:::c
|
||||||
INODE tar_finddir(INODE dir, const char *name)
|
INODE tar_finddir(INODE dir, const char *name)
|
||||||
{
|
{
|
||||||
tree_node_t *tn = (tree_node_t *)dir->data;
|
tree_node_t *tn = (tree_node_t *)dir->data;
|
||||||
@ -170,6 +175,7 @@ it at a few places... Someday I will clean up all my memory leaks.
|
|||||||
Anyway, `finddir` also finds the right node in the tarfs tree and puts
|
Anyway, `finddir` also finds the right node in the tarfs tree and puts
|
||||||
it in the `data` field of the inode.
|
it in the `data` field of the inode.
|
||||||
|
|
||||||
|
:::c
|
||||||
uint32_t read_tar(INODE node, void *buffer, uint32_t size, uint32_t offset)
|
uint32_t read_tar(INODE node, void *buffer, uint32_t size, uint32_t offset)
|
||||||
{
|
{
|
||||||
tree_node_t *tn = (tree_node_t *)node->data;
|
tree_node_t *tn = (tree_node_t *)node->data;
|
||||||
@ -194,10 +200,12 @@ it in the `data` field of the inode.
|
|||||||
Now, all I need to do in order to make read-only files accessible to my
|
Now, all I need to do in order to make read-only files accessible to my
|
||||||
kernel is put them in a directory and run
|
kernel is put them in a directory and run
|
||||||
|
|
||||||
|
:::bash
|
||||||
$ tar -cf tarfs.tar tarfs/*
|
$ tar -cf tarfs.tar tarfs/*
|
||||||
|
|
||||||
and then make sure `tarfs.tar` is loaded as a multiboot module by qemu
|
and then make sure `tarfs.tar` is loaded as a multiboot module by qemu
|
||||||
|
|
||||||
|
:::bash
|
||||||
$ qemu-system-i386 -kernel kernel -initrd tarfs.tar
|
$ qemu-system-i386 -kernel kernel -initrd tarfs.tar
|
||||||
|
|
||||||
or by adding a line to the grub `menu.lst` file.
|
or by adding a line to the grub `menu.lst` file.
|
||||||
|
@ -9,6 +9,7 @@ syscalls are required.
|
|||||||
|
|
||||||
Those are:
|
Those are:
|
||||||
|
|
||||||
|
:::c
|
||||||
void *sbrk(int incr);
|
void *sbrk(int incr);
|
||||||
int getpid();
|
int getpid();
|
||||||
int fork();
|
int fork();
|
||||||
@ -27,6 +28,7 @@ The user space one makes use of the [process memory
|
|||||||
manager](/blog/2013/06/Even-More-Memory/) to return
|
manager](/blog/2013/06/Even-More-Memory/) to return
|
||||||
a chunk of new memory for the `malloc` functions.
|
a chunk of new memory for the `malloc` functions.
|
||||||
|
|
||||||
|
:::c
|
||||||
void *usr_sbrk(int incr)
|
void *usr_sbrk(int incr)
|
||||||
{
|
{
|
||||||
process_t *p = current->proc;
|
process_t *p = current->proc;
|
||||||
@ -54,6 +56,7 @@ a chunk of new memory for the `malloc` functions.
|
|||||||
|
|
||||||
The kernel space version is just a simple linear allocator
|
The kernel space version is just a simple linear allocator
|
||||||
|
|
||||||
|
:::c
|
||||||
uintptr_t kmem_top = KERNEL_HEAP_START;
|
uintptr_t kmem_top = KERNEL_HEAP_START;
|
||||||
uintptr_t kmem_ptr = KERNEL_HEAP_START;
|
uintptr_t kmem_ptr = KERNEL_HEAP_START;
|
||||||
void *sbrk(int incr)
|
void *sbrk(int incr)
|
||||||
@ -79,6 +82,7 @@ user one has a different name.
|
|||||||
###getpid
|
###getpid
|
||||||
`getpid` is rather obvious:
|
`getpid` is rather obvious:
|
||||||
|
|
||||||
|
:::c
|
||||||
int getpid()
|
int getpid()
|
||||||
{
|
{
|
||||||
return current->proc->pid;
|
return current->proc->pid;
|
||||||
@ -87,6 +91,7 @@ user one has a different name.
|
|||||||
###fork
|
###fork
|
||||||
`fork` clones the current process and starts a new thread of execution.
|
`fork` clones the current process and starts a new thread of execution.
|
||||||
|
|
||||||
|
:::c
|
||||||
int fork()
|
int fork()
|
||||||
{
|
{
|
||||||
process_t *child = fork_process();
|
process_t *child = fork_process();
|
||||||
@ -100,6 +105,7 @@ user one has a different name.
|
|||||||
`_exit` stops a program and wakes up any processes that are sleeping on
|
`_exit` stops a program and wakes up any processes that are sleeping on
|
||||||
it.
|
it.
|
||||||
|
|
||||||
|
:::c
|
||||||
void _exit(int rc)
|
void _exit(int rc)
|
||||||
{
|
{
|
||||||
process_t *p = current->proc;
|
process_t *p = current->proc;
|
||||||
@ -125,6 +131,7 @@ its parent process has executed a `wait` syscall.
|
|||||||
Actually, I didn't quite implement `wait` yet, but instead use
|
Actually, I didn't quite implement `wait` yet, but instead use
|
||||||
a `waitpid` for now, which is a bit more specific:
|
a `waitpid` for now, which is a bit more specific:
|
||||||
|
|
||||||
|
:::c
|
||||||
int waitpid(int pid)
|
int waitpid(int pid)
|
||||||
{
|
{
|
||||||
process_t *proc = get_process(pid);
|
process_t *proc = get_process(pid);
|
||||||
@ -162,6 +169,7 @@ is:
|
|||||||
First of all, the executable is found. If it doesn't exist, we want to
|
First of all, the executable is found. If it doesn't exist, we want to
|
||||||
fail as early as possible - before we destroy everything.
|
fail as early as possible - before we destroy everything.
|
||||||
|
|
||||||
|
:::c
|
||||||
int execve(char *name, char **argv, char **env)
|
int execve(char *name, char **argv, char **env)
|
||||||
{
|
{
|
||||||
INODE executable = vfs_namei(name);
|
INODE executable = vfs_namei(name);
|
||||||
@ -176,6 +184,7 @@ The arguments and environment are null-terminated lists of strings
|
|||||||
stored in user space, so they have to be copied into kernel space before
|
stored in user space, so they have to be copied into kernel space before
|
||||||
the user space is destroyed:
|
the user space is destroyed:
|
||||||
|
|
||||||
|
:::c
|
||||||
...
|
...
|
||||||
usigned int envc = 0;
|
usigned int envc = 0;
|
||||||
char **temp_env = 0;
|
char **temp_env = 0;
|
||||||
@ -198,6 +207,7 @@ the user space is destroyed:
|
|||||||
Next, Delete all memory from the previous executable and [load the new
|
Next, Delete all memory from the previous executable and [load the new
|
||||||
one](/blog/2013/08/Loading-Elf/):
|
one](/blog/2013/08/Loading-Elf/):
|
||||||
|
|
||||||
|
:::c
|
||||||
procmm_removeall(current->proc);
|
procmm_removeall(current->proc);
|
||||||
load_elf(executable);
|
load_elf(executable);
|
||||||
current->r.eax = current->r.ebx = current->r.ecx = \
|
current->r.eax = current->r.ebx = current->r.ecx = \
|
||||||
@ -206,6 +216,7 @@ one](/blog/2013/08/Loading-Elf/):
|
|||||||
We need to put the arguments and environment back into the new
|
We need to put the arguments and environment back into the new
|
||||||
executable's user space, so a new stack area is created:
|
executable's user space, so a new stack area is created:
|
||||||
|
|
||||||
|
:::c
|
||||||
new_area(current->proc, USER_STACK_TOP, USER_STACK_TOP, \
|
new_area(current->proc, USER_STACK_TOP, USER_STACK_TOP, \
|
||||||
MM_FLAG_WRITE | MM_FLAG_GROWSDOWN | MM_FLAG_ADDONUSE, \
|
MM_FLAG_WRITE | MM_FLAG_GROWSDOWN | MM_FLAG_ADDONUSE, \
|
||||||
MM_TYPE_STACK);
|
MM_TYPE_STACK);
|
||||||
@ -214,6 +225,7 @@ executable's user space, so a new stack area is created:
|
|||||||
|
|
||||||
Then, copy the environment and arguments onto the stack:
|
Then, copy the environment and arguments onto the stack:
|
||||||
|
|
||||||
|
:::c
|
||||||
if(env)
|
if(env)
|
||||||
{
|
{
|
||||||
pos = pos - envc*sizeof(char *)/sizeof(uint32_t) - 1;
|
pos = pos - envc*sizeof(char *)/sizeof(uint32_t) - 1;
|
||||||
@ -234,6 +246,7 @@ Then, copy the environment and arguments onto the stack:
|
|||||||
And finally, push the argument count, argument list and environment list
|
And finally, push the argument count, argument list and environment list
|
||||||
onto the stack:
|
onto the stack:
|
||||||
|
|
||||||
|
:::c
|
||||||
pos = pos - 3;
|
pos = pos - 3;
|
||||||
pos[0] = (uint32_t)argc - 1;
|
pos[0] = (uint32_t)argc - 1;
|
||||||
pos[1] = (uint32_t)argv;
|
pos[1] = (uint32_t)argv;
|
||||||
@ -249,6 +262,7 @@ This pushes argc, argv and env as arguments to the executabl. We can
|
|||||||
use this to set up the `environ` variable of newlib. The crt0 in newlib
|
use this to set up the `environ` variable of newlib. The crt0 in newlib
|
||||||
pushes `ecx` to the stack and then calls `_init` which looks like this:
|
pushes `ecx` to the stack and then calls `_init` which looks like this:
|
||||||
|
|
||||||
|
:::c
|
||||||
extern char **environ;
|
extern char **environ;
|
||||||
void _init(uint32_t *args)
|
void _init(uint32_t *args)
|
||||||
{
|
{
|
||||||
|
@ -17,6 +17,7 @@ I'll just look at `signal` and `kill` and ignore things like
|
|||||||
First of all, in order to compile newlib with kernel supported signals,
|
First of all, in order to compile newlib with kernel supported signals,
|
||||||
you need the line
|
you need the line
|
||||||
|
|
||||||
|
:::make
|
||||||
newlib_cflags="${newlib_cflags} -DSIGNAL_PROVIDED"
|
newlib_cflags="${newlib_cflags} -DSIGNAL_PROVIDED"
|
||||||
|
|
||||||
in your entry in `newlib/configure.host` as described in [the osdev
|
in your entry in `newlib/configure.host` as described in [the osdev
|
||||||
@ -24,6 +25,7 @@ wiki](http://wiki.osdev.org/OS_Specific_Toolchain#Signal_handling).
|
|||||||
|
|
||||||
Then you need the syscalls:
|
Then you need the syscalls:
|
||||||
|
|
||||||
|
:::c
|
||||||
sig_t signal(int signum, sig_t handler);
|
sig_t signal(int signum, sig_t handler);
|
||||||
int kill(int pid, int sig);
|
int kill(int pid, int sig);
|
||||||
|
|
||||||
@ -38,6 +40,7 @@ as I go along later.
|
|||||||
The way I chose to implement signals was through a list for each
|
The way I chose to implement signals was through a list for each
|
||||||
process. When a signal is sent using `kill` or similar, a `signal_t`
|
process. When a signal is sent using `kill` or similar, a `signal_t`
|
||||||
|
|
||||||
|
:::c
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32_t sig;
|
uint32_t sig;
|
||||||
@ -47,6 +50,7 @@ process. When a signal is sent using `kill` or similar, a `signal_t`
|
|||||||
|
|
||||||
is created and added to the processes list:
|
is created and added to the processes list:
|
||||||
|
|
||||||
|
:::c
|
||||||
int signal_process(int pid, int signum)
|
int signal_process(int pid, int signum)
|
||||||
{
|
{
|
||||||
process_t *p = get_process(pid);
|
process_t *p = get_process(pid);
|
||||||
@ -75,6 +79,7 @@ The `signal` syscall lets the process select how to handle a certain
|
|||||||
signal. Each process also contains a table of `sig_t` and the `signal`
|
signal. Each process also contains a table of `sig_t` and the `signal`
|
||||||
syscall calls the following function:
|
syscall calls the following function:
|
||||||
|
|
||||||
|
:::c
|
||||||
sig_t switch_handler(int signum, sig_t handler)
|
sig_t switch_handler(int signum, sig_t handler)
|
||||||
{
|
{
|
||||||
...
|
...
|
||||||
@ -94,6 +99,7 @@ as the process continues running should be soon enough.
|
|||||||
|
|
||||||
I chose to hook into the my interrupt handler:
|
I chose to hook into the my interrupt handler:
|
||||||
|
|
||||||
|
:::c
|
||||||
registers_t *idt_handler(registers_t *r)
|
registers_t *idt_handler(registers_t *r)
|
||||||
{
|
{
|
||||||
...
|
...
|
||||||
@ -125,6 +131,7 @@ If the handler is set to `SIG_DFL` the function looks up the correct
|
|||||||
default signal handler in a table and calls it. It may be one of the
|
default signal handler in a table and calls it. It may be one of the
|
||||||
following:
|
following:
|
||||||
|
|
||||||
|
:::c
|
||||||
void sighandler_ignore(int num)
|
void sighandler_ignore(int num)
|
||||||
{
|
{
|
||||||
(void)num;
|
(void)num;
|
||||||
@ -144,6 +151,7 @@ following:
|
|||||||
|
|
||||||
If the handler is a function a new thread is created to handle it:
|
If the handler is a function a new thread is created to handle it:
|
||||||
|
|
||||||
|
:::c
|
||||||
...
|
...
|
||||||
sig_t handler = th->proc->signal_handler[signal->sig];
|
sig_t handler = th->proc->signal_handler[signal->sig];
|
||||||
thread_t *h = new_thread((void (*)(void))handler, 1);
|
thread_t *h = new_thread((void (*)(void))handler, 1);
|
||||||
|
@ -25,6 +25,7 @@ I've described how I use tmux with qemu, gdb and telnet
|
|||||||
pane which displays the debug information, but one thing at a time.
|
pane which displays the debug information, but one thing at a time.
|
||||||
Here's what I did in a bash script:
|
Here's what I did in a bash script:
|
||||||
|
|
||||||
|
:::bash
|
||||||
tmux split-window -h 'qemu-system-i386 -kernel build/kernel/kernel -initrd "build/tarfs.tar" -curses -monitor telnet:localhost:4444,server -s -S -serial file:serial.out'
|
tmux split-window -h 'qemu-system-i386 -kernel build/kernel/kernel -initrd "build/tarfs.tar" -curses -monitor telnet:localhost:4444,server -s -S -serial file:serial.out'
|
||||||
|
|
||||||
This opens a new tmux pane with qemu running my kernel and loading
|
This opens a new tmux pane with qemu running my kernel and loading
|
||||||
@ -36,12 +37,14 @@ from serial port 1 to a file.
|
|||||||
|
|
||||||
The next step is:
|
The next step is:
|
||||||
|
|
||||||
|
:::bash
|
||||||
tmux split-window -v 'tail -f serial.out | util/colorize.sh'
|
tmux split-window -v 'tail -f serial.out | util/colorize.sh'
|
||||||
|
|
||||||
which opens up another tmux pane that displays the serial output using
|
which opens up another tmux pane that displays the serial output using
|
||||||
the auto-updating feature of `tail`. The script `colorize.sh` colorizes
|
the auto-updating feature of `tail`. The script `colorize.sh` colorizes
|
||||||
certain words of the output:
|
certain words of the output:
|
||||||
|
|
||||||
|
:::bash
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
C_NO=`echo -e "\\033[0m"`
|
C_NO=`echo -e "\\033[0m"`
|
||||||
@ -60,6 +63,7 @@ certain words of the output:
|
|||||||
|
|
||||||
Next is
|
Next is
|
||||||
|
|
||||||
|
:::bash
|
||||||
tmux select-pane -L
|
tmux select-pane -L
|
||||||
tmux split-window -v 'i586-elf-gdb'
|
tmux split-window -v 'i586-elf-gdb'
|
||||||
tmux select-pane -U
|
tmux select-pane -U
|
||||||
@ -82,6 +86,7 @@ The ones that say
|
|||||||
|
|
||||||
The data for this is stored in a special file called `version.c`
|
The data for this is stored in a special file called `version.c`
|
||||||
|
|
||||||
|
:::c
|
||||||
#include <version.h>
|
#include <version.h>
|
||||||
|
|
||||||
char * __kernel_git_hash = GITHASH;
|
char * __kernel_git_hash = GITHASH;
|
||||||
@ -95,6 +100,7 @@ The data for this is stored in a special file called `version.c`
|
|||||||
|
|
||||||
which has a special line in the kernel makefile:
|
which has a special line in the kernel makefile:
|
||||||
|
|
||||||
|
:::make
|
||||||
version.o: CFLAGS += -DGITHASH='$(shell git log -1 --pretty="tformat:%h")' \
|
version.o: CFLAGS += -DGITHASH='$(shell git log -1 --pretty="tformat:%h")' \
|
||||||
-DGITDATE='$(shell git log -1 --pretty="tformat:%cd")' \
|
-DGITDATE='$(shell git log -1 --pretty="tformat:%cd")' \
|
||||||
-DGITDIRTY='$(shell [[ -n `git status -s 2> /dev/null` ]] && echo 1 || echo 0)' \
|
-DGITDIRTY='$(shell [[ -n `git status -s 2> /dev/null` ]] && echo 1 || echo 0)' \
|
||||||
@ -104,6 +110,7 @@ which has a special line in the kernel makefile:
|
|||||||
A few more lines makes sure `version.c` is always recompiled if any
|
A few more lines makes sure `version.c` is always recompiled if any
|
||||||
other part of the kernel is:
|
other part of the kernel is:
|
||||||
|
|
||||||
|
:::make
|
||||||
kernel: $(kernel_objects)
|
kernel: $(kernel_objects)
|
||||||
rm -f version.o
|
rm -f version.o
|
||||||
$(MAKE) version.o
|
$(MAKE) version.o
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
layout: post
|
layout: post
|
||||||
title: "DITo - Framework"
|
title: "DITo - Framework"
|
||||||
subtitle: "the Disk Image TOols"
|
subtitle: "the Disk Image TOols"
|
||||||
tags: [osdev]
|
tags: [osdev, filesystems]
|
||||||
|
|
||||||
In my osdeving, I was starting to reach the point where a disk driver
|
In my osdeving, I was starting to reach the point where a disk driver
|
||||||
seemed like the obvious next step. This was pretty much entirely unknown
|
seemed like the obvious next step. This was pretty much entirely unknown
|
||||||
@ -42,6 +42,7 @@ some parts from scratch. Let's go!
|
|||||||
The basic operations of DITo are reading from or writing to image files
|
The basic operations of DITo are reading from or writing to image files
|
||||||
or disk drives. Each drive type has a driver
|
or disk drives. Each drive type has a driver
|
||||||
|
|
||||||
|
:::c
|
||||||
typedef struct drive_driver
|
typedef struct drive_driver
|
||||||
{
|
{
|
||||||
int (*open)(struct drive_t *d, int flags);
|
int (*open)(struct drive_t *d, int flags);
|
||||||
@ -53,6 +54,7 @@ or disk drives. Each drive type has a driver
|
|||||||
The drive type contains a pointer to the driver and a pointer to some
|
The drive type contains a pointer to the driver and a pointer to some
|
||||||
arbitrary data used by the driver.
|
arbitrary data used by the driver.
|
||||||
|
|
||||||
|
:::c
|
||||||
typedef struct drive_t
|
typedef struct drive_t
|
||||||
{
|
{
|
||||||
struct drive_driver *d;
|
struct drive_driver *d;
|
||||||
@ -62,6 +64,7 @@ arbitrary data used by the driver.
|
|||||||
Then there are some wrapper functions for performing the required
|
Then there are some wrapper functions for performing the required
|
||||||
operations:
|
operations:
|
||||||
|
|
||||||
|
:::c
|
||||||
int drive_open(struct drive_t *d, int flags)
|
int drive_open(struct drive_t *d, int flags)
|
||||||
{
|
{
|
||||||
if(d->d->open)
|
if(d->d->open)
|
||||||
@ -78,6 +81,7 @@ The next important part of DITo is the filesystem handling. After
|
|||||||
thinking about it, the important primitive functions for all file
|
thinking about it, the important primitive functions for all file
|
||||||
operations I could think about are all in a filesystem driver struct:
|
operations I could think about are all in a filesystem driver struct:
|
||||||
|
|
||||||
|
:::c
|
||||||
struct fs_driver
|
struct fs_driver
|
||||||
{
|
{
|
||||||
INODE (*open)(struct fs_t *fs, const char *path, int flags);
|
INODE (*open)(struct fs_t *fs, const char *path, int flags);
|
||||||
@ -96,6 +100,7 @@ operations I could think about are all in a filesystem driver struct:
|
|||||||
The `fs_t` type contains a pointer to the driver, a pointer to the drive
|
The `fs_t` type contains a pointer to the driver, a pointer to the drive
|
||||||
and a general data pointer.
|
and a general data pointer.
|
||||||
|
|
||||||
|
:::c
|
||||||
typedef struct fs_t
|
typedef struct fs_t
|
||||||
{
|
{
|
||||||
struct fs_driver *driver;
|
struct fs_driver *driver;
|
||||||
@ -109,6 +114,7 @@ as the `drive_*` functions.
|
|||||||
The `INODE` type is a pointer to a struct containing a pointer to the
|
The `INODE` type is a pointer to a struct containing a pointer to the
|
||||||
filesystem, a unique inode number and a pointer to arbitrary data.
|
filesystem, a unique inode number and a pointer to arbitrary data.
|
||||||
|
|
||||||
|
:::c
|
||||||
struct ino_st
|
struct ino_st
|
||||||
{
|
{
|
||||||
fs_t *fs;
|
fs_t *fs;
|
||||||
@ -129,6 +135,7 @@ if I have one image of an FAT floppy disk with a file I want copied to
|
|||||||
the ext2 formated second partition of a hard drive image, I could do
|
the ext2 formated second partition of a hard drive image, I could do
|
||||||
someting like this:
|
someting like this:
|
||||||
|
|
||||||
|
:::c
|
||||||
drive_t *fat_disk = image_drive("floppy.img");
|
drive_t *fat_disk = image_drive("floppy.img");
|
||||||
drive_open(fat_disk, READ_FLAG);
|
drive_open(fat_disk, READ_FLAG);
|
||||||
drive_t *ext2_disk = image_drive("harddrive.img");
|
drive_t *ext2_disk = image_drive("harddrive.img");
|
||||||
@ -164,6 +171,7 @@ someting like this:
|
|||||||
Which of couse will eventually become its own tool so that the actual
|
Which of couse will eventually become its own tool so that the actual
|
||||||
work the end user has to do becomes:
|
work the end user has to do becomes:
|
||||||
|
|
||||||
|
:::bash
|
||||||
$ dito-cp floppy.img:/path/to/file harddrive.img:2:/new/path
|
$ dito-cp floppy.img:/path/to/file harddrive.img:2:/new/path
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
layout: post
|
layout: post
|
||||||
title: "DITo - Drives"
|
title: "DITo - Drives"
|
||||||
subtitle: "Exploring the MBR"
|
subtitle: "Exploring the MBR"
|
||||||
tags: [osdev]
|
tags: [osdev, filesystems]
|
||||||
|
|
||||||
Let's write a few drive drivers.
|
Let's write a few drive drivers.
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ First of all, let's think of what data we'd need to store. The filename
|
|||||||
might be nice to have, and probably a pointer to the opened file. Would
|
might be nice to have, and probably a pointer to the opened file. Would
|
||||||
hate to lose that...
|
hate to lose that...
|
||||||
|
|
||||||
|
:::c
|
||||||
struct im_data
|
struct im_data
|
||||||
{
|
{
|
||||||
char *filename;
|
char *filename;
|
||||||
@ -24,6 +25,7 @@ hate to lose that...
|
|||||||
The functions required for the driver would then make use of the
|
The functions required for the driver would then make use of the
|
||||||
c standard library:
|
c standard library:
|
||||||
|
|
||||||
|
:::c
|
||||||
int im_open(drive_t *d, int flags)
|
int im_open(drive_t *d, int flags)
|
||||||
{
|
{
|
||||||
struct im_data *data = d->data;
|
struct im_data *data = d->data;
|
||||||
@ -51,6 +53,7 @@ c standard library:
|
|||||||
|
|
||||||
The function for setting up the drive is equally simple:
|
The function for setting up the drive is equally simple:
|
||||||
|
|
||||||
|
:::c
|
||||||
drive_driver_t im_driver =
|
drive_driver_t im_driver =
|
||||||
{
|
{
|
||||||
im_open,
|
im_open,
|
||||||
@ -85,6 +88,7 @@ The MBR starts with 436 bytes of space for a bootloader, then there's a
|
|||||||
|
|
||||||
The entries of the table has the following structure:
|
The entries of the table has the following structure:
|
||||||
|
|
||||||
|
:::c
|
||||||
struct MBR
|
struct MBR
|
||||||
{
|
{
|
||||||
uint8_t boot_indicator;
|
uint8_t boot_indicator;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
layout: post
|
layout: post
|
||||||
title: "DITo - Ext2"
|
title: "DITo - Ext2"
|
||||||
subtitle: "A beginning..."
|
subtitle: "A beginning..."
|
||||||
tags: [osdev]
|
tags: [osdev, filesystems]
|
||||||
|
|
||||||
It's finally time to start looking at an actual filesystem.
|
It's finally time to start looking at an actual filesystem.
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ permalink: /about/
|
|||||||
|
|
||||||
Thomas Lovén
|
Thomas Lovén
|
||||||
------------
|
------------
|
||||||
<img src="/media/img/thomas.png" width="300" class="right">
|
{: width="300" .right}
|
||||||
|
|
||||||
Born in Karlskrona in the south of Sweden. Lived there for 18 years
|
Born in Karlskrona in the south of Sweden. Lived there for 18 years
|
||||||
before doing army service in Eksjö and then moving to Gothenburg for
|
before doing army service in Eksjö and then moving to Gothenburg for
|
||||||
|
@ -40,7 +40,6 @@ I earned my bachelors degree in 2012.
|
|||||||
FTF140 Termodynamik och statistisk fysik 7,5hp
|
FTF140 Termodynamik och statistisk fysik 7,5hp
|
||||||
TIF100 Tillämpad kvantfysik 4,5hp
|
TIF100 Tillämpad kvantfysik 4,5hp
|
||||||
FFM232 Vektorfält och klassisk fysik 4,5hp
|
FFM232 Vektorfält och klassisk fysik 4,5hp
|
||||||
{: .prettyprint .lang-betyg}
|
|
||||||
|
|
||||||
###Bachelors thesis
|
###Bachelors thesis
|
||||||
For our bachelors thesis me, two of my fellow students at Engineering
|
For our bachelors thesis me, two of my fellow students at Engineering
|
||||||
@ -70,7 +69,6 @@ As of January 2013 I have just begun work on my Masters thesis.
|
|||||||
FFR120 Simulation of complex systems 7,5hp
|
FFR120 Simulation of complex systems 7,5hp
|
||||||
FFR105 Stochastic optimization algorithms 7,5hp
|
FFR105 Stochastic optimization algorithms 7,5hp
|
||||||
MVE080 Vetenskaplig visualisering 7,5hp
|
MVE080 Vetenskaplig visualisering 7,5hp
|
||||||
{: .prettyprint .lang-betyg}
|
|
||||||
|
|
||||||
__Matematik och samhälle__ translates to __Mathematics and society__.
|
__Matematik och samhälle__ translates to __Mathematics and society__.
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ DEBUG = True
|
|||||||
FLATPAGES_AUTO_RELOAD = DEBUG
|
FLATPAGES_AUTO_RELOAD = DEBUG
|
||||||
FLATPAGES_EXTENSION = '.md'
|
FLATPAGES_EXTENSION = '.md'
|
||||||
FLATPAGES_ROOT = 'pages'
|
FLATPAGES_ROOT = 'pages'
|
||||||
|
FLATPAGES_MARKDOWN_EXTENSIONS = ['codehilite', 'extra']
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.from_object(__name__)
|
app.config.from_object(__name__)
|
||||||
|
@ -2,14 +2,20 @@
|
|||||||
|
|
||||||
{% block page %}
|
{% block page %}
|
||||||
<div id="categories">
|
<div id="categories">
|
||||||
|
{% if title %}
|
||||||
<a href="{{url_for("blog_default")}}">All</a>
|
<a href="{{url_for("blog_default")}}">All</a>
|
||||||
|
{% else %}
|
||||||
|
All
|
||||||
|
{% endif %}
|
||||||
{% for tag in tags %}
|
{% for tag in tags %}
|
||||||
|
{% if tag == title %}
|
||||||
|
-
|
||||||
|
{{ tag }}
|
||||||
|
{% else %}
|
||||||
-
|
-
|
||||||
<a href="{{url_for("tag", tag=tag)}}">{{ tag }}</a>
|
<a href="{{url_for("tag", tag=tag)}}">{{ tag }}</a>
|
||||||
{% endfor %}
|
|
||||||
{% if title %}
|
|
||||||
{{ title }}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<ol class="content-list">
|
<ol class="content-list">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user