Update markdown and tabs and stuff

This commit is contained in:
Thomas Lovén 2016-10-22 17:07:50 +02:00
parent 67e817490e
commit a27daafa0a
39 changed files with 2249 additions and 2139 deletions

View File

@ -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}
![Sample](/media/img/provbrev.jpg) ![Sample](/media/img/provbrev.jpg)

View File

@ -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.

View File

@ -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

View File

@ -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}

View File

@ -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!

View File

@ -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

View File

@ -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.
![PMM1](/media/img/pmm1b.png){: .noborder .center} ![PMM1](/media/img/pmm1b.png){: .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. ![PMM2](/media/img/pmm2b.png){: .noborder .center} the caller.
![PMM2](/media/img/pmm2b.png){: .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. ![PMM3](/media/img/pmm3b.png){: .noborder .center} made up the top of the stack
![PMM3](/media/img/pmm3b.png){: .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

View File

@ -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. ![VMM2](/media/img/vmm2b.png){: .center .noborder} group.
![VMM2](/media/img/vmm2b.png){: .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. ![VMM3](/media/img/vmm3b.png){: .center .noborder} directory too through a fixed memory address.
![VMM3](/media/img/vmm3b.png){: .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

View File

@ -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

View File

@ -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!

View File

@ -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}
&nbsp;
:::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

View File

@ -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

View File

@ -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}
&nbsp;
:::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 }
&nbsp;
:::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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
![PROCMM1](/media/img/procmm1.png){: .center .noborder} ![PROCMM1](/media/img/procmm1.png){: .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.
![PROCMM2](/media/img/procmm2.png){: .center .noborder} ![PROCMM2](/media/img/procmm2.png){: .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.
![PROCMM4](/media/img/procmm4.png){: .center .noborder} ![PROCMM4](/media/img/procmm4.png){: .center .noborder}
Actually, the parent process will probably perform a `waitpid` syscall Actually, the parent process will probably perform a `waitpid` syscall

View File

@ -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)
{ {
... ...

View File

@ -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.

View File

@ -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)
{ {

View File

@ -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?

View File

@ -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.
&nbsp; &nbsp;
:::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);

View File

@ -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");

View File

@ -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];

View File

@ -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.

View 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)
{ {

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -3,7 +3,7 @@ permalink: /about/
Thomas Lovén Thomas Lovén
------------ ------------
<img src="/media/img/thomas.png" width="300" class="right"> ![Me](/media/img/thomas.png){: 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

View File

@ -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__.

View File

@ -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__)

View File

@ -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">