Initial commit - Begun rewrite of website generator

This commit is contained in:
Thomas Lovén 2016-10-21 20:47:34 +02:00
commit 67e817490e
133 changed files with 15248 additions and 0 deletions

BIN
media/2013.pdf Normal file

Binary file not shown.

BIN
media/img/bad_clock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

BIN
media/img/debug_print.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

BIN
media/img/good_clock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 KiB

BIN
media/img/ico_external.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

BIN
media/img/pmm1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

BIN
media/img/pmm1b.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
media/img/pmm2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
media/img/pmm2b.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
media/img/pmm3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

BIN
media/img/pmm3b.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
media/img/procmm1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
media/img/procmm2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
media/img/procmm3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
media/img/procmm4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
media/img/provbrev.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
media/img/thomas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 KiB

BIN
media/img/vmm1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

BIN
media/img/vmm1b.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
media/img/vmm2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

BIN
media/img/vmm2b.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
media/img/vmm3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
media/img/vmm3b.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
media/img/z80_pinout.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

BIN
media/img/z80_pinout2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
media/img/z80_running1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 KiB

BIN
media/img/z80_running2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 KiB

BIN
media/img/z80_running3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 KiB

BIN
media/img/z80_tester.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

View File

@ -0,0 +1,165 @@
layout: post
title: "LaTeX Letter Template"
subtitle: "Simple and clean"
tags: [latex]
I'm looking for a job for next year to get out of university for a while.
That means I've been writing a lot of letters of introduction lately. To
streamline this, I created a custom LaTeX document class called
personal\_letter.
The class looks as follows:
%
% personal_letter
%
% Created by Thomas Loven on 2010-02-24. Feel free to use.
%
\ProvidesClass{personal_letter}
% Based on article
\LoadClass[a4paper, oneside]{article}
\usepackage{ifthen}
% Define functions for user changeable variables
\makeatletter
\newcommand{\adress}[1]{\def \@adress{#1}}
\newcommand{\telephone}[1]{\def \@telephone{#1}}
\newcommand{\email}[1]{\def \@email{#1}}
\newcommand{\name}[1]{\def \@name{#1}}
\newcommand{\place}[1]{\def \@place{#1}}
%\newcommand{\date}[1]{\def \@date{#1}}
\newcommand{\greeting}[1]{\def \@greeting{#1}}
\newcommand{\closing}[1]{\def \@closing{#1}}
\newcommand{\url}[1]{\def \@url{#1}}
\adress{}
\telephone{}
\email{}
\name{}
\place{}
\date{}
\greeting{}
\closing{}
\url{}
\makeatother
% Include usefull packages
\usepackage[utf8]{inputenc}
\usepackage{fullpage}
\usepackage{fancyhdr}
\usepackage{setspace}
\usepackage[swedish]{babel}
\usepackage{longtable}
% Make graphics work with pdf or dvi files
\usepackage{ifpdf}
\ifpdf
\usepackage[pdftex]{graphicx}
\DeclareGraphicsExtensions{.pdf, .jpg, .tif}
\else
\usepackage{graphicx}
\DeclareGraphicsExtensions{.eps, .jpg}
\fi
% Costruct heading
\makeatletter
\fancyfoot[LO, LE]{\@adress}
\fancyfoot[RO, RE]{ \ifthenelse{\equal{\@telephone}{}}{}{ \@telephone \\ \@email \\ \@url }}
\fancyfoot[C]{}
\setlength{\headheight}{0 cm}
\setlength{\headsep}{0 cm}
\renewcommand{\headrulewidth}{0pt}
\addtolength{\textheight}{0 cm}
\renewcommand{\footrulewidth}{0.5pt}
\pagestyle{fancy}
\makeatletter
%Body of the letter
\makeatletter
\newenvironment{body}% {
\begin{quotation}
\begin{onehalfspace}
\setlength{\parskip}{1ex}
\ifthenelse{\equal{\@place}{}}{}{ \begin{flushright} \@place \\ \@date \end{flushright} }
\vspace{2ex}
\bf
\noindent
\@greeting
\rm
}%
{
\vspace{10pt}
\ifthenelse{\equal{\@closing}{}}{}{ \noindent \@closing \\ \noindent \@name } \end{onehalfspace} \end{quotation}
}
\makeatother
\newcommand{\titel}[1]{ \begin{centering} {\Huge #1} \end{centering} }
\newcommand{\rubrik}[1]{ \vspace{1.5em}
{\large \bf #1}
}
{: .prettyprint .lang-tex}
Plain and simple.
And a usage example:
\documentclass{personal_letter}
\name{Thomas Lovén}
\place{Göteborg}
\date{6 april 2010}
\adress{Thomas Lovén \\ Xxxxxxxxxxxxxxx XX \\ XXX XX Xxxxxx Xxxxxxxx}
\telephone{+XX XX XXX XX XX}
\email{thomasloven@gmail.com}
\url{thomasloven.wordpress.com}
\begin{document}
\greeting{Salvete!}
\closing{Di vos incolumes custodiant.}
\begin{body}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
non arcu non massa accumsan tincidunt. Suspendisse non est quis
massa sollicitudin faucibus. Quisque gravida vulputate nisi pharetra
ultrices. Fusce tincidunt ante quis lacus adipiscing eget dictum
justo luctus. Vivamus nec tempus diam. Vivamus rhoncus varius arcu,
et vulputate purus aliquam eget. Cras eget suscipit lectus. Donec
nec nulla ac urna ultricies bibendum sed vitae nibh. Suspendisse
consectetur luctus quam eget vulputate. Pellentesque et nisl et quam
vestibulum auctor quis et metus. Sed cursus tellus at felis lobortis
ut tincidunt purus porttitor.
Donec gravida metus eu dui rutrum nec bibendum libero molestie.
Aenean et odio massa. Donec pulvinar augue non tellus vulputate nec
congue justo accumsan. Nam pretium sagittis dictum. Sed semper auctor
neque in commodo. Mauris dignissim ante ac nibh pretium consequat.
Donec orci tortor, pharetra non congue vel, ultrices sit amet lacus.
Suspendisse a lacus nec ante venenatis bibendum vitae id dui. Nam
semper arcu facilisis nunc euismod volutpat. Donec accumsan velit nec
ante lacinia pulvinar. Phasellus ut varius enim. Pellentesque vel
augue odio. Suspendisse sed nisi vel magna euismod semper. Maecenas
erat neque, tristique id consequat id, mollis eu enim. Phasellus
laoreet pulvinar ante accumsan posuere. Proin viverra dui id ipsum
hendrerit non mollis mi rutrum.
\end{body}
\end{document}
{: .prettyprint .lang-tex}
![Sample](/media/img/provbrev.jpg)

View File

@ -0,0 +1,80 @@
layout: post
title: "Restarting a hoby kernel"
subtitle: "Once more, from the top... "
tags: [osdev]
Many years ago, I got interesting in trying out Linux. Having used Windows
since my parents started using Windows computers at work and declared our old
Macintoshes obsolete I realized it would mean quite a change and didn't really
want to go all in right away. Somehow I found out about VMWare Workstation - an
Intel processor emulator made for running virtual computers within Windows and
installed one of the first versions of Ubuntu in it.
I played around with Ubuntu Linux for a while, but then I started thinking
about the computer emulator I was using and wondered whether I could make
a program myself that would boot in it. In other words, could I write a program
that ran by itself on a computer with no operating system?
The answer turned out to be yes. With a lot of help and tutorials from what
later became [osdev.org](http://www.osdev.org) and many more that
I unfortunately can't remember or find anymore, I wrote a small binary
executable in Assembly that could run of the boot sector on a floppy disk.
I was amazed! Could it really be this simple? I immediately set out to write an
entire operating system. Nothing fancy; I just wanted to boot the computer and
get into a desktop environment with a text editor and a compiler so I could
keep adding to the system from within it. Shouldn't be too hard, should it?
Almost ten years later, I think I got the routines for basic BIOS-assisted
screen printing down...
There are several reasons why things are moving so slowly. First and foremost:
It wasn't that easy. Operating systems are advanced stuff, and combined with my
own lack of any formal education or experience in programming or computer
systems this means progress is slow.
Next is a lack of time. Back when I found VMWare Workstation and started this
journey, I was on summer holiday from school and had all the time in the world.
Then school started again. Then came the Christmas holiday and I realized I had
forgotten everything. So I threw everything out and started again from scratch.
This time I got a bit further. Then school started again.
And that's how it's been since then. School started, ended and begun again.
Then I did a year in the army. Then university (engineering physics). Then
I met a girl, got engaged, bought a house, worked for a year with project
management, went back to university... Still, every now and then I've restarted
development of my operating system dream.
So, how will I ever get out of this reset loop? Well, I've got a plan. Most
important, I think, is to have realistic expectations. That shouldn't be as
much a problem as it used to be. As time has passed I have grown to understand
my limitations better than I did when I was 15. Also, my main goal of
programming now is recreation. A moment of peace by myself when I can
concentrate on a problem I appreciate. If I never get a stable virtual file
system going, that's ok. If I build a working tcp-ip stack and get a web server
running under my own operating system, that's ok too, but it's not my goal.
Still, this doesn't mean I want to keep rewriting the code for setting up the
Interupt Vector Table for the x-th time next time I have a week of university,
which brings me to the next part of the plan.
Documentation. I'm going to restart my development efforts once again, but this
time I plan to be more careful about documentation. My thought is that if
I write down what I do and why, I won't have to do it again in six months but
can just read through my old notes. Who knows; this plan might just be crazy
enough to work.
I've obviously tried this before. It worked somewhat but I made a big mistake.
I called my notes a "tutorial". I did that because I wished it to be
a tutorial, that beginners could learn from my years of mistakes. Some people
didn't like this and a few unnecesarily harsh comments got me of the osdev
scene for a while. Then university started again and I was back in the loop.
This time I won't make that mistake. I still wish to make those notes public,
though, because I still think that I and possibly others may benefit from them.
I'll be clear though, that I am not a profesional programmer and have never
pretended or wanted to be one. I appreciate feedback in the case anyone reads
this and I can be reached through email or twitter (links below). Please try to
stay constructive, though.
Anyway, this is already way more text than anyone would bother to read, so
I guess I'll just cut it off here and we'll see where this series of notes
finally takes us.

42
pages/2012-06-13-Bochs.md Normal file
View File

@ -0,0 +1,42 @@
layout: post
title: "Bochs"
subtitle: "In two versions"
tags: [osdev]
For testing out my os during development, I usually use
[Bochs](http://bochs.sourceforge.net)
I also usually compile Bochs into two versions. One that has the debugger
option enabled and one that runs entirely in a command line environment.
Let's go!
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
mkdir 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
{: .prettyprint .lan-sh}
And that's the terminal version. ### Important note 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.
mv /usr/local/bin/bochs /usr/local/bin/bochs-term
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
make all
make install
{: .prettyprint .lan-sh}
Finally, copy the bochs bioses to a public place
cp -r ../bochs-2.5.1/bios /usr/share/bochs
{: .prettyprint .lan-sh}
And that's it.

View File

@ -0,0 +1,75 @@
layout: post
title: "Cross compiler"
subtitle: "Setting up under OSX Lion"
tags: [osdev]
For simplicity, I chose to set up a so-called cross compiler for osdeving. In
OSX Lion, this is what I did.
First of all, I installed Xcode from the Mac App store. Since version 4.3,
Xcode doesn't install any command line tools anymore, so this had to be done
manually:
- Open Xcode
- Go the Preferences
- Choose Downloads
- Find Command Line Tools and click Install.
This is better than some earlier versions, though, which use a buggy
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
[Homebrew](http://mxcl.github.com/homebrew/).
brew install mpfr
{: .prettyprint}
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
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/mpfr/mpfr-3.1.0.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
code posted in my logs will work for you (it's very likely to work, but not
*guaranteed*). Gmp, mpfr and mpc are floating point libraries that are used by
gcc, so after extracting all archives, they are simply copied into the gcc
source
mv gmp-5.0.2 gcc-4.6.3/gmp
mv mpfr-3.1.0 gcc-4.6.3/mpfr
mv mpc-0.9 gcc-4.6.3/mpc
{: .prettyprint .lang-sh}
In order not to mess up the source, binutils and gcc were built out of tree.
mkdir build-binutils
cd build-binutils
export PREFIX=/usr/local/cross
export TARGET=i386-elf
../binutils-2.22/configure --target=$TARGET --prefix=$PREFIX --disable-nls
make all
make install
{: .prettyprint .lang-bash}
And the same for gcc, using the new binutils
cd ..
mkdir build-gcc
cd build-gcc
export PATH=$PATH:$PREFIX/bin
../gcc-4.6.3/configure --target=$TARGET --prefix=$PREFIX --disable-nls --enable-languages=c --without-headers
make all-gcc
make install-gcc
{: .prettyprint .lang-bash}
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
manage to get it to actually compile...
And that's it!

View File

@ -0,0 +1,120 @@
layout: post
title: "Setting up"
subtitle: "Preparing the environment for OsDev"
tags: [osdev]
So it's time to start.
I have previously set up a cross compiler environment on my Mac mini, and am
now developing on it connected via ssh.
First point today was making a directory structure and creating a [git
repository](http://github.com/thomasloven/os5). The directory structure starts
off as follows
os5/
|-- build/
|-- include/
`-- kernel/
`-- include/
A difference from previous times is that the _include_ directory is outside the
kernel directory. That's because last time I realized I had a lot of include
files that were used both in the kernel and many other parts. I'll probably
move it into some library directory later. Know what? Let's move it into
a library directory right now...
os5/
|-- build/
|-- kernel/
| `-- include/
`-- library/
`-- include/
The build/ directory contains some scripts needed for building and testing the
os as well as a floppy image preinstalled with the GRUB bootloader.
Next, basic Makefiles were added to os5/ and kernel/. The standard procedure of
the main makefile is to run the makefile in kernel/, copy the kernel image into
the floppy and then run bochs-term.
BUILDDIR := $(PWD)
PATH := /usr/local/cross/bin:$(PATH)
DIRS := kernel
TARGET := i386-elf
AS := nasm
CC := i386-elf-gcc
LD := i386-elf-ld
ASFLAGS := -f elf
CCFLAGS := -nostdlib -nostdinc -fno-builtin -fno-exceptions -m32
CCFLAGS += -fomit-frame-pointer -fno-asynchronous-unwind-tables
CCFLAGS += -fno-unwind-tables -I$(BUILDDIR)/library/include
LDFLAGS := -T $(BUILDDIR)/library/include/Link.ld
export BUILDDIR AS CC LD ASFLAGS CCFLAGS LDFLAGS
.SILENT:
.PHONY: $(DIRS) floppy emul
.default: all floppy emul
l: all floppy emul
all: $(DIRS)
$(DIRS): force
@echo " MAKE " $@
@cd $@; $(MAKE) $(MFLAGS)
clean:
@for DIR in $(DIRS); do echo " CLEAN " $$DIR; cd $(BUILDDIR)/$$DIR; make clean; done;
floppy: force
@echo " UPDATING IMAGE"
@build/update_image.sh
emul: force
@echo " STARTING EMULATOR"
@build/emul.sh
force:
true
{: .prettyprint}
The makefile in the kernel/ directory is pretty much straight forward.
TARGET := kernel
SUBDIR := kernel
SOURCES := kinit.o boot.o
SOURCES += $(patsubst %.s,%.o,$(shell find . -name "*.s" | grep -v boot.s))
SOURCES += $(patsubst %.c,%.0,$(shell find . -name "*.c" | grep -v kinit.c))
CCFLAGS += -Iinclude
LDFLAGS := -T $(BUILDDIR)/$(SUBDIR)/include/Link.ld
.SUFFICES: .o .s .c
all: $(TARGET)
$(TARGET): $(SOURCES)
@echo " ln " $(TARGET)
@$(LD) $(LDFLAGS) -o $(TARGET) $(SOURCES)
.c.o:
@echo " gcc " $<
@$(CC) $(CFLAGS) -c $< -o $@
.s.o:
@echo " nasm " $<
@$(AS) $(ASFLAGS) $< -o $@
clean:
-@rm $(SOURCES) 2>/dev/null
-@rm $(TARGET) 2>/dev/null
{: .prettyprint}

View File

@ -0,0 +1,237 @@
layout: post
title: "Booting Procedure"
subtitle: "Starting up the x86"
tags: [osdev]
[Previous part](/blog/2012/06/Setting-Up/)
To boot up the operating system kernel, I use
[GRUB](http://www.gnu.org/software/grub/). It takes care of things like getting
into protected mode, checking the memory and activating processor flags. It can
also load any file you ask it to into memory - which is good, because we won't
see a disk driver here anytime soon - before starting the loaded kernel.
I want to write a kernel that resides in a high part of memory (0xC0000000 and
above) because I think it looks tidy. In order to load a high part kernel
without paging, I use the trick described at
[osdev.org](http://wiki.osdev.org/Higher_Half_bare_bones). This requires
a special Linker file for the kernel.
*kernel/include/Link.ld*
ENTRY(start)
SECTIONS {
. = 0xC0100000;
.text : AT(ADDR(.text) - 0xC0000000)
{
code = .; _code = .; __code = .;
*(.text)
*(.eh_frame)
. = ALIGN(4096);
}
.data : AT(ADDR(.data) - 0xC0000000)
{
data = .; _data = .; __data = .;
*(.data)
*(.rodata)
. = ALIGN(4096);
}
.bss : AT(ADDR(.bss) - 0xC0000000)
{
bss = .; _bss = .; __bss = .;
*(.bss)
. = ALIGN(4096);
}
_end = .;
}
{: .prettyprint .linenums}
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
the first order of business. This is done in the assembly bootstrap.
*kernel/boot.s*
; Kernel start point
[global start]
start:
cli
; Load page directory and enable paging
mov ecx, BootPageDirectory - KERNEL_OFFSET
mov cr3, ecx
mov ecx, cr0
or ecx, 0x80000000
mov cr0, ecx
lea ecx, [.higherHalf]
jmp ecx
.higherHalf:
; Load GDT
mov ecx, gdt_ptr
lgdt [ecx]
SetSegments 0x10, cx
jmp 0x8:.gdtLoaded
.gdtLoaded:
{: .prettyprint .lang-nasm .linenums:61}
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
loads cx into each segment selector register. It is defined in an included file
which also contains some constants.
*kernel/include/asm_macros.inc*
; GRUB multiboot headers
MBOOT_PAGE_ALIGNED_FLAG equ 1<<0
MBOOT_MEMORY_INFO_FLAG equ 1<<1
MBOOT_HEADER_MAGIC equ 0x1BADB002
MBOOT_HEADER_FLAGS equ MBOOT_PAGE_ALIGNED_FLAG | MBOOT_MEMORY_INFO_FLAG
MBOOT_HEADER_CHECKSUM equ -(MBOOT_HEADER_FLAGS + MBOOT_HEADER_MAGIC)
KERNEL_OFFSET equ 0xC0000000
BOOT_STACK_SIZE equ 0x1FFF
; SetSegments 0x10 ax loads all segment selectors with 0x10 using eax
%macro SetSegments 2
mov e%2, %1
mov ds, %2
mov es, %2
mov fs, %2
mov gs, %2
mov ss, %2
%endmacro
{: .prettyprint .lang-nasm .linenums:2}
There are also references to some data structures, i.e. *BootPageDirectory* and
*gdt_ptr*. Those are hardcoded in the bootstrap file.
*kernel/boot.s*
%include "include/asm_macros.inc"
[bits 32]
section .bss
align 0x8
; Stack for booting
[global BootStack]
BootStackTop:
resb BOOT_STACK_SIZE
BootStack:
section .data
align 0x1000
; Page directory for booting up.
; First four megabytes are identity mapped as well as
; mapped to 0xC0000000
[global BootPageDirectory]
BootPageDirectory:
dd (BootPageTable - KERNEL_OFFSET) + 0x3
times ((KERNEL_OFFSET >> 22) - 1) dd 0x0
dd (BootPageTable - KERNEL_OFFSET) + 0x3
times (1022 - (KERNEL_OFFSET >> 22)) dd 0x0
dd (BootPageDirectory - KERNEL_OFFSET) + 0x3
BootPageTable:
%assign i 0
%rep 1024
dd (i << 12) | 0x3
%assign i i+1
%endrep
; Hard-coded GDT.
; GDT pointer is wrapped into the first entry
[global gdt]
gdt_ptr:
gdt:
dw 0x002F
dd gdt
dw 0x0000
dd 0x0000FFFF, 0x00CF9A00
dd 0x0000FFFF, 0x00CF9200
dd 0x0000FFFF, 0x00CFFA00
dd 0x0000FFFF, 0x00CFF200
section .text
align 4
; GRUB Multiboot data
MultiBootHeader:
dd MBOOT_HEADER_MAGIC
dd MBOOT_HEADER_FLAGS
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
directory which has the same page table at virtual memory 0x00000000 and
0xC0000000. The rest of the page directory is cleared. The page table maps the
first four megabytes of physical memory. The hard-coded GDT has five entries.
The first one is always empty, so this space is used to store the gdt pointer.
Then there's kernel code, kernel data, user code and user data. Each segment
has base 0 and size 4 gb, so they all contain all of the virtual memory space.
Finally, there's the multiboot header for GRUB. This has to be at the very
start of the .data section, so it is also loaded first by the makefile.
The last thing we need before we can go into a higher level language is
a stack, but let's take this opportunity to also remove the identity mapping
from the page directory.
*kernel/boot.s*
.gdtLoaded:
; Clear the identity mapping from the page directory
mov edx, BootPageDirectory
xor ecx, ecx
mov [edx], ecx
invlpg[0]
; Load a stack for booting
mov esp, BootStack
mov ebp, BootStack
; eax contains the magic number from GRUB 0x2BADB002
push eax
; ebx contains the address of the Multiboot information structure
add ebx, KERNEL_OFFSET
push ebx
; Call the c function for setting up
[extern kinit]
call kinit
jmp $
{: .prettyprint .lang-nasm .linenums:83}
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
structure location respectively.
The only thing that's left now in order to get this to run is the c stub.
*kernel/kinit.c*
void kinit()
{
}
{: .prettyprint .linenums:2}
Compiling and running this through bochs, we are presented with a black and
white screen completely void of error messages. Perfect!
The code up to this point can be found in my github repository.
Commit [66dd86fc12](https://github.com/thomasloven/os5/tree/66dd86fc128e2714e4c93c73d8a0bf8542e10573)

View File

@ -0,0 +1,63 @@
layout: post
title: "C headers in Asm"
subtitle: "Cleaning up the build chain"
tags: [osdev]
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
EXACT_PI equ 3
{: .prettyprint .lang-nasm}
and in c
#define EXACT_PI 3
{: .prettyprint .lang-c}
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.
Normally, when you run a c compiler, it makes multiple passes over your source
file. The first one or two times, it runs a pre-processor. The preprocessor
checks for things like _#include_ and _#define_ and replaces macros. The next
pass actually compiles the code. Then the compiler invokes a linker and so on.
What I found out today is that you can run only the preprocessor and it will
replace all the preprocessor code and ignore the rest. In other words, you can
use c preprocessor macros in assembler. Awesome!
So, how is this done?
Well, here's a minimal (non-working) example:
_myAsmFile.asm_
#include <header.h>
mov eax, EXACT_PI
{: .prettyprint .lang-nasm}
_include/header.h_
#pragma once
#define EXACT_PI 3
#ifndef __ASSEMBLER__
// This is not evaluated if header.h is included from an assembly file.
#endif
{: .prettyprint .lang-c}
This is compiled through:
cpp -I include -x assembler-with-cpp myAsmFile.asm -o myAsmFile.s
nasm myAsmFile.s
{: .prettyprint}
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
preprocessor commands.
An alternative to _cpp_ is _gcc -E_. Actually, this is often exactly the same
thing...
This is implemented in git commit [742f2348ec](https://github.com/thomasloven/os5/tree/742f2348ecc58eaa8239b06c666bd8c3c539c019).

View File

@ -0,0 +1,32 @@
layout: post
title: "Kernel debug functions"
subtitle: "Can't live without them"
tags: [osdev]
I read through the comments on my old OsDev series the other day and got stuck
on one of them.
> "\[...\] I noticed that you handle printing in the kernel, of all places. A kernel isn't supposed to do video output.\[...\]"
I totally and whole-heartedly agree with this, and I really really wish I could
code so well I didn't feel a need for outputting a single debug message before
I had finished the booting procedure, physical and virtual memory management,
interrupts, faults, thread and process handling, scheduling, system calls,
message passing, virtual file system, a device driver framework, file writing
library functions and a terminal driver. Alas, I am not that good, and
therefore I find it helpful to print a message to the screen every now and
then. Wheter the kernel's supposed to do video ouptut depends on the kind of
kernel anyway.
But enough with this immature passive aggressiveness. Let's do some kernel
handled printing!
Screen printing is usually quite simple. Most personal x86-based computers can
do it by simply writing the text to a certain area in memory. I'm not going to
waste very much time on it but pretty much just copy the code from an earlier
project and move on to more interesting things.
I also added some useful library functions and macros like min(a,b), max(a,b)
and swap(a,b) and a few parts of the c standard library. It can all be found
in Git commit
[16fdacb89](https://github.com/thomasloven/os5/tree/164fdacb896b3427633433f97bbd12d779a3d1f3).

View File

@ -0,0 +1,72 @@
layout: post
title: "Memory Page Stack"
subtitle: "Basic memory management 1/3"
tags: [osdev]
###Memory management
One of the most important tasks that the operating system has it to distribute
the resources of the computer. One of those are the memory.
I like to divide the memory management of the kernel into three main parts:
- Physical memory management - Keeps track of the pages of actual memory
- Virtual memory management - Keeps track of the virtual address spaces
- Heap management - Fine grained control of memory for kernel use only
This post is about a part of the physical memory manager. This simple version
keeps track of free pages of physical memory. When asked for a page, it hands
it out and forgets about it until it is handed back.
###Memory page stack
Pointers to the unused memory pages are stored on a stack and just popped off
when needed.
The stack does of course take up an amount of memory, but through an
interesting trick, this doesn't matter. You see, the memory where the stack is
stored is made up of memory pages and if enough pages are handed out to clear
an entire page in the stack, that page is next to be handed out.
Allow me to illustrate:
Imagine a computer with really really small memory pages - so small in fact
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 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.
![PMM1](/media/img/pmm1b.png){: .noborder .center}
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
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
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
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,
that page is used to increase the stack space. Through the use of virtual
memory and paging, the stack doesn't have to be contiguous in physical memory,
so any page can make up a part of the stack.
###Filling the stack
When the computer is booted up, the bootloader is asked to give the kernel
a map of the available memory. This is used to initialize the stack. The kernel
simply goes through the memory map returns each unused page it finds to the
pmm.
###Improvements
There's a lot more that can be done by the pmm. One major feature that will be
added at a later state is keeping track of how many users each page has. This
is useful if we wish to introduce shared memory or copy-on-write during forks.
Otherwise we risk that a page is returned and handed out again while someone
still think they have exclusive access to it. Not a good thing.
###Git
This page stack has been implemented in Git commit
[caed8cb8a0](https://github.com/thomasloven/os5/tree/caed8cb8a0e39a1e7d7d2594b86f25b887afab81).
Also implemented in the same commit is a virtual memory manager with recursive
page directories described in [this
post](/blog/2012/06/Recursive-Page-Directory).
__Note__: There's a bug in that commit. In short, when filling the stack, the pmm looks for memory map entries between `assert_higher(mmap_start)` and `mmap_start + mmap_size`. Where the `assert_higher` macro pretty much just adds `0xC0000000` to the adress. Well... I'm sure you see what the problem is.
The bug has been rectified in Git commit [ebaae73](https://github.com/thomasloven/os5/tree/ebaae7383bbadbfc3de62b1b14aa9a450d8e695c).

View File

@ -0,0 +1,109 @@
layout: post
title: "Recursive Page Directory"
subtitle: "Basic memory management 2/3"
tags: [osdev]
###Memory management part 2
As discussed in [Memory Page Stack](/blog/2012/06/Memory-Page-Stack) one can
divide memory management into three parts.
This post regards the second part, the Virtual Memory Manager (VMM).
###Paging
In the x86 architecture, one normally uses a two-step paging algorithm.
In the [previous post](/blog/2012/06/Memory-Page-Stack) we imagined a computer
with really really small memory pages. Now imagine that this computer has an
even equally small virtual memory space - 16 pages in total. Those 16 pages are
divided into four groups and this is our key to addressing them.
![VMM1](/media/img/vmm1b.png){: .center .noborder}
Above, we see the address space of our imagined computer illustrated as 16 blue
squares. Let's say the processor wishes to access the sixth page and that
paging is enabled. The sixth page belongs to the second group of pages and is
the second page in that group.
Now the Memory Management Unit (MMU) of the processor kicks in and takes a look
in a certain processor register. This register contains the (physical) address
of the Page Directory (step I). Since we want the second group, it reads the
second entry from the page directory and this gives the physical address of
a page table (step II). We want the second page in that group, so the MMU reads
the second entry in the page table and this gives the physical address of the
memory page we want (step III).
What makes paging so great is that the whole process is completely transparent
to the processor. The user will never know when a memory read or write
operation crosses a page boundary, and by changing the entries in the page
directory and tables you can get a contiguous memory area anywhere in the
virtual address space without having to worry about fragmentation of the
physical memory.
###Recursive page directory
Paging lets you easily decide what parts of memory the processor has access to
and it can be dynamically changed by changing the contents of the page
directory and page tables. Of course, that requires you to keep track of where
those are, but a neat little trick makes that really easy.
The trick consists of setting the last entry in the page directory to point to
the page directory itself.
Let's say we did this, and that the processor wishes to access the 14th page in
virtual memory - that is the second page in the fourth group. The MMU starts by
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,
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
group. ![VMM2](/media/img/vmm2b.png){: .center .noborder}
In other words, you can access any page table through a fixed address in
memory. But wait, it gets even better.
Let's look at what happens if we try to access the very last page in the
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
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
directory too through a fixed memory address. ![VMM3](/media/img/vmm3b.png){: .center .noborder}
###Some considerations
An important question to put at this point is whether a recursive page
directory is really a good idea.
In our imaginary computer with its really small address space, we notice that
the page table and directories now reserve a quarter of the entire available
virtual memory - which is of course incredibly wasteful. On a computer with
a 32 bit address bus the reserved area would be 1/1024 th of the available
address space, though, which is more reasonable. Then again, if your computer
has 4 gigabytes of physical RAM, this would mean there is four megabytes of it
that can't be used. Then again again, if you have easy access to your page
tables - such as through a recursive page directory - you can just page in
those 4 megabytes as needed.
There are other simple ways of accessing the page directory and tables. For
example, if you just keep track of the page directory and one page table it's
a simple job to page in another table anywhere and read it.
The recursive page directory divides the OsDev community. I think it's a nice
tool, others don't.
###The addresses
Finally, if a recursive page directory is used on an x86, the following can be
used to access the page directories and tables:
uint32_t *page_dir = 0xFFFFF000;
uint32_t *page_tables = 0xFFC00000;
//addr = virtual address
//phys = physical address (page alligned)
//flags = access flags
page_dir[addr >> 22] = &page_tables[addr >> 12] | flags;
page_tables[addr >> 12] = phys | flags;
{:.prettyprint}
###Git
A recursive page directory has been implemented in Git commit
[caed8cb8a0](https://github.com/thomasloven/os5/tree/caed8cb8a0e39a1e7d7d2594b86f25b887afab81).
Also implemented in the same commit is a physical memory manager with a free
page stack described in [this post](/blog/2012/06/Memory-Page-Stack)

View File

@ -0,0 +1,110 @@
layout: post
title: "Memory Heap"
subtitle: "Basic memory management 3/3"
tags: [osdev]
###Memory management part 3
I have previously ([Memory Page Stack](/blog/2012/06/Memory-Page-Stack),
[Recursive Page Directory](/blog/2012/06/Recursive-Page-Directory)) described
how I like to divide the memory management of a kernel into three parts. This
post regards the third part, which I like to call the heap.
###Memory Management
In fact, a memory heap is but one way of handling memory. I believe it's the
kind that is easiest to implement and understand (with one exception) but it is
a bit slow.
The main task of a memory manager is to reserve and hand out areas in memory
when asked. For example, a program may run _malloc(size)_ and get a pointer to
a memory area of the asked size. It can then do whatever it wants with this
memory area and can be sure that it will not be handed out again by _malloc_
before it has been returned by _free_.
Now, let's look at a few ways to do this.
###Linear allocation
The simplest possible memory manager just hands out a new address each time
_malloc_ is called and doesn't care when memory is freed.
uint32_t memory_pointer = HEAP_START;
void *malloc(uint32_t size);
{
memory_pointer = memory_pointer + size;
return memory_pointer - size;
}
void free(void *mem)
{
;
}
{: .prettyprint}
###Heap
The next method - which I prefer - is a memory heap.
The heap consists of a list of free memory areas. In the simplest possible
variety, it would look something like this:
struct area_header
{
uint32_t size;
struct free_area *next;
};
void *malloc(uint32_t size)
{
struct area_header *area = heap_list_head;
while(area)
{
if(area->size >= size)
{
remove_from_heap_list(area);
return get_memory_area(area);
}
area = area->next;
}
panic("Out of memory!");
}
void free(void *mem)
{
struct area_header area = get_area_header(mem);
insert_into_heap_list(area);
}
{: .prettyprint}
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
memory area is allocated, the caller of _malloc_ gets a pointer to the end of
the area header. The area header thus remains untouched by the program until
the memory is freed again.
The memory could then look something like below.
Blue areas in the figure are free and red ones are allocated. The header of
each free memory area has a pointer to the next free area.
###Improvements
Some improvements can be made to this simple scheme. First, if no free area is
big enough, the heap can be increased by adding a new area with the right size
to the end. Alternatively, two free areas that are right next to each other
can be joined together to form a bigger one.
Also, it would be a good idea if an area is bigger than necessary to split it.
Finally, it's a lot easier to keep track of everything if the memory areas are
doubly linked and if allocated areas are not removed from the list but just
marked as used.
###Git
With those improvements the scheme is similar to what I've implemented in Git
commit
[8db335ce3b](https://github.com/thomasloven/os5/tree/8db335ce3bed30c8e75275c2fc96a2b697106023).
###Further improvements
Some more improvements can be made to the heap in order to increase
performance. The most common ones use different ways to search for free areas
of the correct size. For example, the free areas could be stored in an ordered
list starting with the smallest, or they could be stored in several lists
depending on their size.

View File

@ -0,0 +1,104 @@
layout: post
title: "Vim Macros"
subtitle: "and interrupt handling"
tags: [osdev]
###The problem
Today I was writing some code for handling interrupts.
At one point I needed the following piece of code
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
Vim macros.
I've been using this site and my rewrite of my operating system as an excuse to
learn vim. And today it payed off. To write the above piece of code I used the
key presses
iisr0(void),<esc>0qayyp3l<ctrl>a0q46@a47k48J$r;Iextern void <esc>
Couldn't be easier!
OK, so maybe it could... Let's break it down.
Let's start with
iisr0(void),<esc>
_i_ puts vim in Insert mode. There we write _isr0(void),_ and finally leave
Insert mode with the escape key.
Next is _0_ to bring the pointer to the beginning of the line. Then comes the
macro.
qayyp3l<ctrl>a0q
_qa_ starts recording a macro into register a.
_yyp_ yanks the current line and pastes it below.
_3l_ skips over the i, s and r.
Ctrl+a increases the number under the pointer by one.
Finally _0_ goes back to the beginning of the line and _q_ stops the macro
recording.
The next part:
46@a47k48J
runs the macro 46 times, steps up 47 times and joins the current line with the
next 48 times. We now have
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
_$r;_ and insert _extern void_ at the beginning of the line using _I_.
###Another example of macros
Starting with
INTNOERR 0
{: .prettyprint}
I used
qayypcwINTNOERR<esc>$<ctrl>a0q
qsyypcwINTERR<esc>$<ctrl>a0q
dd6@a@s@a5@s33@a
and ended up with
INTNOERR 0
INTNOERR 1
INTNOERR 2
INTNOERR 3
INTNOERR 4
INTNOERR 5
INTNOERR 6
INTNOERR 7
INTERR 8
INTNOERR 9
INTERR 10
INTERR 11
INTERR 12
INTERR 13
INTERR 14
INTNOERR 15
...
INTNOERR 45
INTNOERR 46
{: .prettyprint}
I love vim!
###Application
So where did I use this? I've been writing some code for handling interrupts in
the os. You can find it in Git commit
[26dd8e4c75](https://github.com/thomasloven/os5/tree/26dd8e4c7507b66e4f94bf2c4e980265c6f0a20b).

View File

@ -0,0 +1,80 @@
layout: post
title: "iPhoto on Older iDevices"
subtitle: "Front-Facing camera? Pfft!"
###iPhoto for iOS
In the shadow of the new iPad, Apple finally released a version of iPhoto for
iOS. The problem is it's only for iPhone4, iPad2 and later. I own an iPhone3GS
and a first generation iPad, and when I try to install it on them, they tell me
that it requires a front-facing camera.
The front-facing camera requirement is of course absolute BS. The real
restriction is the RAM of the device. iPhone 4 and iPad2 has 512 MB of RAM,
twice as much as the 3GS and the original iPad. I assume that the camera
restriction is just something that was in the app store already, and that's why
Apple used it.
###Common solution
It didn't take long for someone to find out that the camera restriction could
be circumvented if you buy the app in iTunes on your computer and then install
it using Apples own _iPhone Configuration Utility_. This does work, and iPhoto
runs great and surprisingly smoothly on both the original iPad and the 3GS
(though with occasional crashes due to lack of memory).
The problems turn up when you try to sync with iTunes. A dialog box will pop up
asking you if you want to authorize your device with iTunes (or something to
the effect). Selecting No will remove iPhoto and all your work. Some people got
it to work when selecting Yes, but I never did. Instead syncing just stops.
###My solution (requires jailbreak)
The key to my solution is the error message
>This app requires a front facing camera.
To find out whether the device has a front facing camera or not it looks into
the property list of _Springboard_-the main interface of iOS. This is located
at
/System/Library/CoreServices/Springboard.app/
and is called either N??AP.plist or K??AP.plist on an iPhone or an iPad
respectively where ?? is a number that seems to vary with your model. In there
it looks for a property called _front-facing-camera_ and if it exists and is
set to true, it decides that your device has a front facing camera.
There are no further checks, and iPhoto doesn't use the camera at all, so all
you need to do in order to install it is add this value to your property list.
I've had no problems synchronizing to iTunes after using this method. iPhoto
does, however, crash at some occasions. The crashes are few and far between,
though.
###A problem
I have found one problem with this method; the iphone 3GS thinks it has a front
facing camera. That means there's a button in the camera app that lets you
switch between the front and back camera. Tapping it will make the camera
screen freeze. The remedy for this is to toggle video mode, and then you can
switch back to the back camera.
###How to do it
- Jailbreak
- Install iFile from Cydia
- in iFile, navigate to _/System/Library/CoreServices/SpringBoard.app/_
- Find your property list (N??AP.plist or K??AP.plist)
- Open it with the Property List Viewer
- Tap Capabilities
- Tap the + in the bottom right corner
- type _front-facing-camera_
- Select Type:Boolean
- Tap Create
- Find your new property and activate it
- tap Done
- Restart springboard or your device
You should now be able to install iPhoto through the app store or iTunes.
###Bonus
While you're in there, adding the property _screen-mirroring_ and enabling it
will let you use official 20-pin-to-vga adaptors...

View File

@ -0,0 +1,158 @@
layout: post
title: "Privilege Levels"
subtitle: "Lots of abbreviations ending in PL"
tags: [osdev]
###Processor privilege level in Segmentation
The Intel x86 processor architecture has a number of features implemented to
protect the system from malicious code. One of those features is the
__Privilege Levels__.
The privilege levels are a remnant of the times when memory segmentation was
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
like
jmp CS:AX
{: .prettyprint .lang-nasm}
the processor looks into the currently loaded __Local__ or __Global Descriptor
Table__ ( __LDT__/ __GDT__) for the entry pointed to by _CS_. This enty (or
__Segment Descriptor__) describes the beginning of a segment which is combined
with the offset in _AX_ to get the physical address;
physical_address = segment_descriptor_from_index(CS).base + AX;
{: .prettyprint}
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__
(or segfault for short). Now you can start to see how this system makes for
a working memory protection scheme. By switching out the LDT, you can change
what part of physical memory is addressed by any Selector:Offset-pair and thus
give each task or process their own address space.
The segmentation scheme is now deprecated in favor of paging which offers more
fine-grained control and a greater level or transparency.
So, what about the privilege levels?
Well, the user program can switch its own segment selector values. However,
each segment has a protection level, given by the __Descriptor Privilege
Level__ ( __DLP__) in the segment descriptor. The processor has a __Current
Privilege Level__ ( __CPL__) which is given by the lowest two bits of _CS_. If
the program tries to switch a selector to a descriptor with a DPL that is lower
than the CPL, the processor throws a __General Protection Fault__.
###Processor privilege levels today
I mentioned that segmentation is deprecated in favor of paging, so why would
I care about it for a modern state-of-the-art operating system such as mine?
Firstly, the x86 architecture requires segmentation to access the entire
address space - most hobby OSes I've studied just keeps two segments (one for
code and one for data - processor requirement) for this, with base 0x0 and
a limit of 4 gb (in other words, they each cover the entire virtual address
space).
Secondly, there are other ways than segmentation where the CPL comes into play.
For example, in paging, if the supervisor bit of a page table entry is set, the
address can only be accessed if the processor is in CPL 0 (sometimes called
__ring 0__).
The privilege levels are also used to determine whether certain instructions
may be run, like _sti_, _lgdt_, _hlt_ and such.
Finally, the privilege levels determine which interrupts may be called with the
_int_ instruction (each interrupt descriptor in the IDT has an assigned DPL).
So there's still a point to keep privilege levels around for your hobby OS,
despite the problems they cause with segmentation and TSS and stuff.
###Changing the privilege level
Changing the CPL is actually two different problems.
- Increasing CPL
- Decreasing CPL
Increasing the CPL is relatively easy. It can be done either through a far jump
JMP 0x1B:label
label:
; The CS selector is now 0x18 | 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
###The IRET instruction
Let's change the topic for a minute and think about interrupts.
Say the processor is running in __Kernel Mode__ (Ring 0, CPL=0) and an
interrupt happens. What the processor does then is:
- Push SS and ESP to stack
- Push EFLAGS to stack
- Push CS and EIP to stack
- Load CS and EIP from the IDT
and from there the interrupt handling routine takes over.
The interrupt handling routine does its thing and then runs the `IRET`
instruction. `IRET` makes the processor do the same thing as when an interrupt
happens, but _backwards_. I.e:
- Pop CS and EIP from stack
- Pop EFLAGS from stack
- Pop SS and ESP from stack
- Do stack stuff
- Far jump to CS:EIP
Notice that extra thing there? The "Do stack stuff"?
At that point, the processor checks the value of CS that is just popped. It
compares the __Requested Privilege Level__ ( __RPL__, last one - promise - I'm
not making these up, you know) in the bottom two bits of this to the CPL and if
it is higher it changes SS and ESP to the recently popped values. This is really
useful for software task switching.
So, you could easily get into a higher privilege level by intercepting
a handled interrupt and changing the value of CS on the stack. If you set the
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
stack and push that onto the stack before running `IRET` .
// C code
struct
{
uint32_t esp;
uint32_t ss;
uint32_t eflags;
uint32_t eip;
uint32_t cs;
} fake_stack;
fake_stack.esp = usermode_stack_top;
fake_stack.ss = user_data_segment | 0x3;
fake_stack.eflags = 0;
fake_stack.eip = &usermode_function;
fake_stack.cs = user_code_segment | 0x3;
set_all_segments(user_data_segment | 0x3);
run_iret(&fake_stack);
{: .prettyprint}
; Assembler code
run_iret:
add esp, 0x8
iret
{: .prettyprint .lang-nasm}
###Going back to ring0
I was going to continue this blog post with talking about how to switch from
a higher CPL to a lower, but it is growing way longer than I thought it would.
Therefore I will cut it off here, and continue in a new post.
###Application
The methods described in this post is used in Git commit
[52a0c84739](https://github.com/thomasloven/os5/tree/52a0c84739e04f3d9dd7410cdf0b378118a946b4).

View File

@ -0,0 +1,97 @@
layout: post
title: "Return To Kernel Mode"
subtitle: "Seriously, what's up with TSS?"
tags: [osdev]
###Processor privilege levels
In [my last post](/blog/2012/07/Privilege-Levels) I wrote about x86 privilege
levels. Remember the CLP, the DPL, the RPL and the KPL? Ok, I made that last
one up, but there actually is one more -PL thing to take into consideration.
The IOPL, but that won't come into play yet.
Anyway, I showed that switching privilege level was kind of easy, if you do it
from a lower value to a higher. I also said that if you try to load a lower
level than your CPL into _CS_ you get a GPF.
###Going to a lower privilege level
So, the processor designers obviously didn't want us to go to a lower
protection level, so why would we? Well, there are some things that we want the
kernel to do that requires a higher protection level. For example we may want
to change the value of _cr3_ during a task switch in order to load a new
address space for the new process.
Luckily, there is a way to get back to ring 0 (kernel mode) and that's through
interrupts. When an interrupt happens (or is called by a program - more on this
at an other time), the processor loads a new value for CS from the relevant
interrupt descriptor in the IDT and - in this case - allows the change in
privilege level no matter which way it goes. If the privilege level is changed
by loading the new CS, the processor looks into the currently loaded __TSS__...
###Task State Segment
The __Task State Segment__ ( __TSS__) is another remnant from the olden days of
the x86 processor architecture.
I'll gladly admit that I don't understand them at all, but it has to do with
hardware task switching. In short, I believe that you can load a number of TSS
entries into the GDT and load one of them into a special register at once to
perform a task switch. The processor would then save all registers into the old
TSS and load them from the new one, all in one instruction. ... or something
like that...
But, as I mentioned above, if the privilege level is lowered during a processor
interrupt, the processor looks into the currently loaded TSS. From there it
loads a new value for SS and ESP. In other words, the TSS determines the stack
position as we get back into kernel mode.
###Loading a TSS
Loading a TSS is actually rather simple and it's kind of hard to go wrong. So
hard in fact that most tutorials actually do. Here's how it's really done
(source: [Intel
Manuals](http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html/))
gdt[TSS_DESCRIPTOR].base = &tss;
gdt[TSS_DESCRIPTOR].limit = sizeof(tss);
gdt[TSS_DESCRIPTOR].flags = 0;
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
segment, and like all memory segments it has a segment descriptor in the GDT
(the TSS descriptor can not be stored in an LDT). Finally, like all segment
descriptors, the TSS descriptor points to a segment that resides between the
address _base_ and _base + limit_. In other words, _limit_ should be the size
of the TSS, not the end of it.
What's the point in that, you might ask. The TSS is of fixed length, isn't it?
Not really; the TSS is sometimes followed by a bitmap which decides which I/O
ports the processor is allowed to access. If such a bitmap exists, the TSS
segment is increased to incorporate it.
###Why you should get this right
The thing is that if you don't have an I/O map, any value for the limit above
0x67 will be accepted by the processor. However, the limit field is only 20
bits long and is often truncated to this length by cutting off the upper 12
bits in the code that sets the descriptor. Most of the time, this works, but
say your TSS begins at address 0xFFFFF and is of minimum length. That means it
ends at 0x100066 and if you truncate this to 20 bits and put it as limit you
get 0x66. Boom! Protection fault! If you're lucky, it happens every time you
boot your kernel. If you're not, you may have a really nasty bug to track down.
###Back to the TSS
When you have your TSS segment descriptor, you need to set up the TSS itself.
Without hardware multitasking, there are only three values of TSS that are
reqired. SS0, ESP0 and IOMAP.
SS0 and ESP0 were described in my previous post and IOMAP is the offset of the
I/O map inside the TSS segment. This needs to be set even if you don't have an
IO map, so I just set it to the length of the TSS structure.
###Application
The methods described in this post is used in Git commit
[52a0c84739](https://github.com/thomasloven/os5/tree/52a0c84739e04f3d9dd7410cdf0b378118a946b4).

View File

@ -0,0 +1,352 @@
layout: post
title: "Thread Stacks"
subtitle: "4 a.m. - know where your stack pointer is?"
tags: [osdev]
Since the x86 architecture has relatively few processor registers, a
programmer may need additional space to store temporary values. For most
compilers and languages, this space is the stack. C, for example, (gcc
and clang at least) uses the stack to store local variables, function
arguments and return addresses. In other words, the stack comes in use
every time there is a function call.
The common way a function call is handled by a c compiler is this:
- Push each argument to the stack (in reverse order)
- Execute the `CALL` instruction (which pushes the address of the next
instruction to the stack and jumps to the callee)
The callee does the following:
- Push the base pointer to the stack
- Sets the base pointer to the current stack pointer
- Subtracts the stack pointer to reserve place for local variables.
- Do its thing
- Increase the stack pointer to free the space used by local variables.
- Pop the base pointer from stack.
- Execute the `RET` instruction (which puts the return value in EAX and
jumps to the position at the top of the stack.
While the callee is doing its thing it now has access to all the pushed
arguments at addresses (ebp + 8) and forwards and all local variables
at addresses up to ebp. The return address is reachable at (ebp + 4) if
you'd ever want that.
This convention makes it really easy to have functions which takes an
undefined number of arguments, like `printf` does.
###Stacks in context switching
It also makes for really simple _context switching_.
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.
This is a common way of making usermode threads. Ponder the following:
void switch_thread()
{
push_all_registers();
switch_stack_pointer();
pop_all_registers();
return;
}
void a()
{
while(1)
{
do_something();
switch_thread();
}
}
void b()
{
while(1)
{
do_something_else();
switch_thread();
}
}
Imagine two threads - __A__ and __B__ running, __A__ runs `a()` and __B__
runs `b()`. Each has a stack somewhere in memory, and __A__ is currently
running. The top of the stacks looks like:
+-----------------------+
|switch_stack_pointer RA|
|all registers |
+----------ESP----------+ |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
{: .nopretty}
where `RA` means Return Address and `ESP` is where the stack pointer is
currently pointing.
As execution of __A__ continues, the processor will `do_something()` and
then call `switch_thread()`...
+-----------------------+
|switch_stack_pointer RA|
+----------ESP----------+ |all registers |
|switch_thread RA | |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
{: .nopretty}
`switch_thread()` pushes all registers to the stack and calls
`switch_stack_pointer()`
+----------ESP----------+ +-----------------------+
|switch_stack_pointer RA| |switch_stack_pointer RA|
|all registers | |all registers |
|switch_thread RA | |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
{: .nopretty}
`switch_stack_pointer()` performs some scheduling to find out which
thread is to run next, and then switches the stack pointer over to the
top of __B__'s stack.
+-----------------------+ +----------ESP----------+
|switch_stack_pointer RA| |switch_stack_pointer RA|
|all registers | |all registers |
|switch_thread RA | |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
{: .nopretty}
The processor keeps on executing code, and `switch_stack_pointer()` soon
returns
+-----------------------+
|switch_stack_pointer RA| +----------ESP----------+
|all registers | |all registers |
|switch_thread RA | |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
{: .nopretty}
`switch_thread()` pops all registers and returns...
+-----------------------+
|switch_stack_pointer RA|
|all registers |
|switch_thread RA | +----------ESP----------+
|a RA | |b RA |
| ... | | ... |
{: .nopretty}
... and we're now in `b()` with all registers of __B__ loaded.
###Stacks in the kernel
When an interrupt or exception happens in user mode, [a new stack is
loaded from the tss](/blog/2012/08/Return-To-Kernel-Mode/) and
(usually) all registers are pushed onto it before the kernel starts the
__Interrupt Service Routine__.
Wait... _all registers are pushed onto it_? I like the sound of that.
That's, like, half the work of changing threads, right? Right!
If you've been following a kernel development tutorial (like [James
Molloys](http://www.jamesmolloy.co.uk/tutorial_html/) or [Brandon
Friesens](http://www.osdever.net/bkerndev/Docs/title.htm)) you probably
have something like this to handle interrupts:
int_stub:
pusha
xor eax, eax
mov ax, ds
push eax
mov eax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call int_handler
pop eax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 8
iret
{: .lang-nasm}
void int_handler(registers_t r)
{
do_stuff();
}
In fact, if you've been following one of those tutorials, you probably
have the above code twice, for some reason...
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
stacks too...
int_stub:
pusha
xor eax, eax
mov ax, ds
push eax
mov eax 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp ;Pass stack pointer to int_handler
call int_handler
mov esp, eax ;int_handler returns a new stack pointer
pop eax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 8
iret
{: .lang-nasm }
registers_t *int_handler(registers_t *r)
{
do_stuff();
r = get_next_thread(r);
return r;
}
This gives a pointer to the threads registers as input to the ISR and
expect a pointer to some registers in return. They may or may not be the
same.
###Keeping track of the stacks
The saved registers are a large part of what defines each thread, but
there are actually a few things more that are needed.
First of all, the kernel may want some extra information associated
with each thread, such as scheduling information and a list of signal
handlers.
Sometimes a thread in user mode will need help from the kernel which
it cannot offer immediately. The thread may for example issue a read
request to a file that's on a drive which has some spin-up time before
it can be read. The kernel may then switch to another thread while the
disk spins up. Therefore it's a good idea to have a separate kernel
stack space for each thread.
With some thought, those three things can be easily combined into a
single data structure. So let's think about it for a while.
While the thread is running we want some information stored somewhere in
kernel space about it.
+-----------------------+
|thread information |
+-----------------------+
{: .nopretty}
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
information it will have to go right before it, since the stack grows
backwards.
+-----------------------+
|thread registers |
|thread information |
+-----------------------+
{: .nopretty}
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
stack from there?
+-----------------------+
| ... |
|kernel mode stack |
|thread registers |
|thread information |
+-----------------------+
{: .nopretty}
###Setting this up
To set this up, the thread information structure has to be set up
something like:
struct thread_info_struct
{
uint8_t stack_space[KERNEL_STACK_SIZE];
registers_t r;
struct thread_data_struct thread_data;
} my_thread_info;
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
end of the registers, i.e. the beginning of the thread data.
TSS.esp0 = &my_thread_info.thread_data;
And that's really all there is to it. Unbelievable, really, how many
years it took for me to figure this out.
In the process, I've found inspiration in [Rhombus by Nick
Johnson](https://github.com/nickbjohnson4224/rhombus/) and
[linux](http://www.linux.org).
###Some considerations
In order to do the actual switching of threads, I implemented a special
syscall which can be called only from kernel mode.
Let's say a user mode program calls `yield()`. This performs a syscall
in the form of an interrupt instruction `INT 0x80` and thus we jump into
the kernel.
The kernel performs some housekeeping and selects a new thread to run.
It then performs the special switching interrupt `INT 0x82`.
Since we're already in kernel mode, no new stack is loaded but the
registers are pushed onto the old one. The top of the kernel stack will
then contain a `registers_t` structure and a pointer to it is saved in
a `kernel_stack` variable in the `thread_data` portion of the thread
information structure.
Next, the thread information structure of the new thread is read and
the `kernel_stack` pointer from it is returned to the `int_stub` as
above. The `IRET` instruction brings us back to wherever we were before
(probably in kernel mode, but could as well be user mode). If the new
thread was swapped out while in kernel mode, it will carry on from
wherever it was and eventually return to user mode.
This way of handling kernel stacks also makes for really clean nesting
of interrupts.
###Usage
This method has been implemented in git commit
[756852fc66](https://github.com/thomasloven/os5/tree/756852fc66b80b1e605
8d74b8dc334ad841ec5ea)
###A warning
I recently learned - the hard way - that the [clang
compiler](http://clang.llvm.org) does not use this calling convention
for functions which do not in turn call other functions. I.e
int double_integer(int a)
{
return 2*a;
}
int main(int argc, char **argv)
{
double_integer(5);
}
If this code is compiled with clang `double_integer` will (in some
cases) not push `ebp` to stack.
This severely hinders many debuggers and should be considered a bug in
my oppinion.

View File

@ -0,0 +1,53 @@
layout: post
title: "10 Kinds of People"
subtitle: "... and some 7 billion more kinds."
We've all read the joke.
> There are 10 types of people in the world... Those who understand
> binary and those who don't."
Or the one people who pride themselves to be a bit more nerdy than the
nerdy ones prefer:
> There are 10 types of people in the world ... Those who understand
> hexadecimal and 15 others."
There's even a quote about this on [bash.org](http://bash.org/?top).
> kow: "There are 10 types of people in the world... those who
> understand binary and those who don't."
> SpaceRain: That's only 2 types of people, kow.
> SpaceRain: STUPID
I'll tell you; this SpaceRain dude has a point.
In fact, I'd say:
### There are 10 types of people in the world... Those who doesn't know how to communicate a number base properly, and nine types who do.
As [xkcd](http://xkcd.com/169/) put it:
> Communicating badly and then acting smug when you're misunderstood is
> not cleverness.
Of course, "In base 2 there are 10 types of people in the world..."
isn't very funny, but - in all fairness - the original joke isn't very
funny anymore either. However, "There are 010 types of people in the
world... Those who can use google to look up octal numbering conventions
and 7 others", is a bit funny to my fancy, even if it's a very strange
way of classifying people.
Maybe we all should just move on from this joke or at least bring it to
the next level.
I'll end this with a version that is actually funny and which I regret
to say I will probably never find the original source of.
> "There are 2 types of people in the world... Those who can extrapolate
> from incomplete data"
Also, I recently added commenting to the blog posts. Please, fill me in
on your versions.

View File

@ -0,0 +1,163 @@
layout: post
title: "Z80 Information"
subtitle: "A humble beginning"
tags: [electronics]
Six or seven years ago, I bought a box full of fun stuff on eBay. It
turned out to include a few Z80 processors. I had just finished my army
duty and still remembered the math and physics lectures from college
(equivalent) where most of the time had been wasted playing Super Mario
on our Texas Instruments TI83+ graphing calculators who run on the very
same processor. So I was well aware on what wonders it's capable of.
Unfortunately, I didn't even have time to start playing with my new
goodies before I went to university and my life turned into a mess.
The time finally came about a year and a half ago. I was having the
first vacation of my life and actually had time to spare.
At this point I had no kind of plan. So I started out by trying to find
out if at least one of the Z80s actually worked.
History of the Z80
------------------
According to [Wikipedia](http://en.wikipedia.org/wiki/Zilog_Z80),
the Z80 processor was launched in 1976 by Zilog. The founder of Zilog
had previously worked for Intel on their 8080 microprocessor. The Z80
is therefore designed to run any code which runs on the former.
Further, the Z80 features an extended instruction set compared to the
8080. The extra instructions include bit and block operations.
The Z80 also has two sets of registers which can be swapped through a
special register, indexed addressing and vectorized interrupts.
I have no experience working with the 8080 and this far only a little
bit of actually programming a Z80. I've already, however, begun to
appreciate some of those features.
What I appreciate even more - at least this far - is the following
hardware features of the Z80 compared to the 8080.
- A single 5V power supply - as opposed to +5V, -5V and +12V for the 8080
- A single clock signal input - as opposed to two for the 8080
What this means
---------------
What those hardware features mean, is that getting a Z80 to run is
actually really simple. All we need in ways of support circuits are a
single square wave clock signal and a reliable reset circuit. Oh, and a
power supply of course.
A step back
-----------
But we shouldn't get ahead of ourselves here. Let's instead take a step
back and look at the Z80.
![Z80 pinout](/media/img/z80_pinout2.png){: .right .noborder}
This is the pinout of a standard Z80 in a 40-pin DIP package. I have
colorized the pins to show one way of grouping their functions.
A line above the pin name or a `/` before it (such as `/INT`) indicates
that the pin is Active Low, i.e. 0V means yes and 5V means no in layman
terms.
###Green
The green pins are the address bus. A0 to A15 are used to indicate an
address in memory during a memory read or write operation. A0 to A7 are
also used to select I/O device during a port read or write operation.
The address bus is output only.
###Red
The red pins are the data bus. D0 to D7 are used to transfer or receive
data during a memory or port read or write operation. The data bus can
also be used to indicate which device fired an interrupt. The data bus
can be both input and output.
###Blue
The blue pins are what keeps the Z80 going. The 5V DC power supply,
a ground connection and a clock signal. We'll look more on the clock
signal later.
###Orange
The orange pins are used to control the Z80.
- `/INT` indicates that a hardware interrupt occurred and makes the Z80
act on this.
- `/NMI` is a Non Maskable Interrupt and has higher priority than the `INT`
- `/WAIT` can be used by memory or I/O devices to make the Z80 wait
during a read or write operation while the device prepares to fill the
request.
- `/BUSRQ` is used by external devices to request control over the data,
address and system control buses. When the Z80 is ready to hand over
control, this is signaled to the requesting device via the `BUSACK`
pin.
- `/RESET` is used to reset the Z80 into a well defined state.
All orange pins are input only.
###Yellow
The yellow pins are used to control peripherals and other parts of the
computer system.
- `/HALT` indicates that the Z80 is in a halted state, i.e. it is waiting
for an interrupt to happen.
- `/MREQ` indicates that the Z80 wishes to access memory.
- `/IORQ` indicates that the Z80 wishes to access an I/O port.
- `/RD` goes low during a memory or I/O read operation.
- `/WR` goes low during a memory or I/O write operation.
- `/BUSACK` indicates that the Z80 has let an other device take control
of the buses.
- `/M1` indicates that the Z80 is fetching the next instruction from
memory. This pin turned out to be incredibly useful for debugging.
- `/RFSH` back in the days memory circuits couldn't keep their contents
indefinitely, even with power on, but had to be given a refresh pulse
every now and then. This usually required some extra circuitry which
kept track of which memory addresses needed refreshing when. The Z80,
however, outputs a refresh signal from time to time and also gives an
address to refresh at A0 to A6(!).
All yellow pins are output only.
Fetch Decode Execute
--------------------
A CPU operates in what's called instruction cycles. The instruction
cycles can be broken down into three sub-cycles, namely the Fetch cycle,
the Decode cycle and the Execute cycle.
During the Fetch cycle, the processor reads an instruction from memory.
It keeps the address of the next instruction to read stored in a special
register, the Program Counter _PC_. The Z80 also outputs the refresh
signal during the end of the Fetch cycle. After reading an instruction
_PC_ in incremented by one.
During the Decode cycle, the processor decodes the read instruction.
If more data needs to be read from memory (e.g. an address in a jump
instruction) it is read now (_PC_ is incremented accordingly).
During the execute cycle, the processor does what the instruction told
it to. It might for example - in the case of a jump instruction - change
the contents of the _PC_ register, or add two registers together.
The cycle then repeats indefinitely or until the processor is halted.
The simplest instruction
------------------------
So... the goal of this post was to make a Z80 tester, right? Actually,
that will probably have to wait for the next post, because this one is
getting really long now. However, it makes sense to look at one last
piece of information right now. To make sure a Z80 is working, we'd
want it to go through a number of instruction cycles. As a first step,
though, we don't actually need it to do anything, but just move on to
the next instruction.
Luckily, there's an instruction for this.
From the [Z80 CPU Users Manual](http://www.z80.info/zip/z80cpu_um.pdf) by Zilog:
> NOP
>
> Description: The CPU performs no operation during this machine cycle.
Sounds just like what we need! To make it even more perfect, the `nop`
instruction is indicated by the byte `0x00` or all zeroes.
I'll let you think about the implications of this until next time.

View File

@ -0,0 +1,172 @@
layout: post
title: "Z80 Tester"
subtitle: "Bouncing buttons"
tags: [electronics]
In the [last post](/2013/01/Z80-Information/) I said we only needed two
pieces of support circuits to get a Z80 running, namely a clock and a
reset circuit.
Let's take a look at the most advanced one first; the clock signal
generator.
![Bad clock generator](/media/img/bad_clock.png){: .right}
Clock signal
------------
The Z80 needs a 5V square clock signal.
For testing purposes it's ok to generate the clock signal by hand. The
easiest way to do this requires only a switch and a resistor configured
like the figure on the right.
Let me just stop you right now and tell you that this won't work.
Why? Well, take a look at the output in an oscilloscope:
![Bouncy signal](/media/img/bad_clock_signal1.png)
Doesn't look so bad, does it? Well, let's take a closer look at that
falling edge to the left. Here it is, zoomed in about 250 times:
![Bouncy signal](/media/img/bad_clock_signal2.png)
Now that's not pretty. If you used this clock circuit, for every time
you press the button, the Z80 will receive a dozen pulses or two. That
makes for some very unrepeatable behavior, and repeatability is what
computers are all about.
![Good clock generator](/media/img/good_clock.png){: .right}
A better clock signal
---------------------
Here's a good clock generator. It requires two switches which are pushed
alternately, two pull-up resistors and two NAND gates (e.g. 74HC00).
This is what's called an S-R-latch (S-R is short for Set-Reset).
Let's say the clock output is currently HIGH (+5V) and that no switches
are pressed. The upper NAND gate then has two HIGH inputs, which means
its output is LOW (0V). The lower NAND has one LOW input and one HIGH,
which means its output is HIGH. By pushing the top button, one of the
inputs to the top NAND is grounded so that its input is (LOW, HIGH)
which means it outputs HIGH. This means the lower NAND gets the input
(HIGH, HIGH) which makes its output LOW and when the button is released,
the top NAND will have the input (HIGH, LOW) and thus still output HIGH.
It's a bit simpler to understand if you just think about it than if you
try to read my messy explanation, but you must assume that the clock
signal is either HIGH or LOW before you start thinking.
![Good clock circuit](/media/img/good_clock_circuit.png){: .left}
Anyway, I built this circuit on a bread board and measured it the same
way as the last one.
![CLean signal](/media/img/good_clock_signal.png)
Nice and clean. We've got our clock source.
Reset switch
------------
When it comes to reset the Z80 is not as picky as with the clock signal.
In fact, we can use the first attempt at a clock signal for reset.
The internal reset circuitry of the Z80 depends on the clock signal,
so as long as the clock doesn't move, whatever happens with the reset
signal doesn't matter.
This fact is important to rember for other reasons too; to perform a
reset, the reset pin must be pulled low for a few clock cycles.
In other words: To perform a reset, push the reset button, toggle the
clock a handful of times (minimum three), release the reset button.
That took me a while to figure out and is why you should always read
your datasheets carefully, kids.
A similar note of caution: Don't leave the clock signal in its LOW state
for any extended periods of time or the Z80 will forget its state. To be
sure, always make the clock HIGH right after making it LOW. You could
add an LED to show its current state.
Let's wire it up
Z80 tester
----------
![Z80 tester wiring](/media/img/z80_tester.png)
Here's a Z80 tester circuit wired up on my breadboard.
The connections are as follows:
- `D0`-`D7` are pulled low through resistors of ~1kΩ.
- `/INT`, `/NMI`, `/WAIT` and `/BUSRQ` are connected to +5V.
- `+5V` and `GND` are connected as labeled.
- `/RESET` is connected to a pull up resistor and a button as described
above.
- `CLK` is connected to an Arduino nano which generates a 500 Hz square
signal (I felt a bit lazy).
- `A0`-`A7` are connected to the logic probes of my oscilloscope.
But before I power this up, let's think about what we're expecting to see.
As we release the reset button (after keeping it down for three clock
cycles or more) the Z80 starts to read instructions at address `0x0000`.
Since all data pins are permanently tied low it will read the value
`0x00` which corresponds to a `NOP` instruction. This means it skips
ahead to address `0x0001` to read its next instruction and so on.
In other words, we expect the 8 lowest address lines to count upwards
binarily from 0 to 255. Since there are 8 address lines we're not
monitoring, it should make this count 255 times before getting back to
address `0x0000` again. But we won't be able to see the difference
between the counts.
Ok, let's take a look.
![Z80 tester running](/media/img/z80_running1.png)
Just like that. This Z80 seems to be working fine.
All 255 addresses didn't quite fit in this picture, but you can see
address `0xXX00` to the left and `0xXX7B` to the right.
Scrolling forwards a bit in time we find something interesting.
![Z80 tester running 2](/media/img/z80_running2.png)
See that thing on `A7`? It's not stable, but is pulsing. The same
behavior can be seen on `A8`-`A15`. But why?
Dynamic memory refresh, that's why.
As I described in the last post; right after fetching an instruction,
the Z80 sends a refresh pulse to the memory chips. During this pulse, it
also sends an address on the 7 lowest address lines, i.e. `A0`-`A6`. The
other address lines are pulled low.
Let's take a closer look at the signals used.
![Z80 tester running 3](/media/img/z80_running3.png)
Here I also connected to the oscilloscope
- `/M1`
- `/MREQ`
- `/RD`
- `CLK`
We can clearly see the instruction fetch cycle. `/M1` goes low, followed
by `/MREQ` and then `/RD`. Then all three goes high while the processor
is decoding the instruction.
During the decode cycle, we see `/MREQ` go low again. This is the memory
refreshing. I didn't connect the `/RFSH` line to the oscilloscope but,
trust me, it goes low at about the same time.
Next is the execute cycle, which for the `NOP` instruction is nothing at
all, so the processor jumps right to the next instruction fetch cycle.
Ok... so the Z80 is indeed working as expected.
Next time we'll look at a control panel.

View File

@ -0,0 +1,37 @@
layout: post
title: "A Step Back"
subtitle: "Exactly why I keep this blog"
tags: [osdev]
I've previously described my normal mode of enjoying the hobby of
operating system development, i.e. restarting from the ground every time
I get some time, and then forgetting everything once school starts.
That's exactly why I started writing down what I'm doing in this blog.
The thing is, I didn't really keep to this at the end of summer, and ran
ahead a bit with the programming, thinking I should catch up with the
blogging at a later time. Obviously, I didn't do this...
When I took a look at the state of my code a week or so ago, I
noticed I hadn't even checked in my changes since August. So that's
what I did, and then I branched off the last commit I blogged about
([756852fc66](https://github.com/thomasloven/os5/tree/756852fc66b80b1e60
58d74b8dc334ad841ec5ea)) and made that the new master branch.
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'
git checkout -b new_master OLD_COMMIT_SHA
git merge --strategy=ours master
git checkout master
git merge new_master
The resulting commit is found at
[f74ec287db](https://github.com/thomasloven/os5/tree/f74ec287db488a7bda5
4ad75f979ca6b5664feef).
If you take a look at my [commit
list](https://github.com/thomasloven/os5/commits/), though, you'll
notice that I cheated again... I told you I would!
Anyway. I'll get right on describing what I've been doing...

View File

@ -0,0 +1,177 @@
layout: post
title: "New Environment"
subtitle: "A bit more and less modern tools"
tags: [osdev]
Two summers ago, I didn't always have access to my build computer, so I
came up with a convoluted development setup which could be run over an
ssh connection.
I would edit files locally using some graphical text editor. Then I
would save my files and wait for them to propagate to the build computer
through Dropbox.
Next I would run the build commands through ssh and then test the
results in bochs terminal mode.
Last summer I started using vim to edit the source directly on the build
computer, which meant I didn't have to wait for Dropbox.
Around the same time I started to find some awesome tools which have
been around for ages.
Those are, for example [tmux](http://tmux.sourceforge.net/) which is a
terminal multiplexer which runs circles around gnu screen in terms of
usability and features.
LLVM
----
I also started looking at the [llvm](http://llvm.org) compiler suite.
The main reason I've been using a cross compiler is that I'm building
under OSX. OSX has its own executable format (Mach-O something or other),
but I'd like to use gnu elf, because it's well documented and there's
lots of example code about. The gcc bundled with OSX only compiles for
OSX, obviously, but OSX recently started shipping with llvm and clang
instead.
Clang can apparently be used as a cross compiler whatever way it was
built, though the available documentation is a bit unclear on how
exactly to do this. Come to think of it, the available documentation is
a lot unclear on just about everything. Funny for a compiler which
boasts expressive error messages as a feature...
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
{: .prettyprint .lang-sh}
`-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
standard does not work...
Since clang version 3.2 `-ccc-host-triple` has apparently been replaced
by `-target`. This change is, of course, not mentioned in the release
notes.
In short, the llvm project - though I appreciate the thought - is an
excellent example of why you should document your code, kids.
This being said. If you know of some secret stash of llvm or clang
documentation, please - PLEASE - let me know!
I still use clang for compiling my kernel, though. Mostly because of the
aforementioned expressive error messages. They really are a nice change
to gcc and in pretty colors too.
If you run OSX clang 3.1 is installed with the
current version of Xcode. Version 3.2 is installed by
[Homebrew](http://mxcl.github.com/homebrew/).
> homebrew install llvm
{: .prettyprint .lang-sh}
Binutils
--------
The linker supplied with llvm doesn't read linker scripts - and I didn't
even get it to link my kernel objects together at all anyway - so I
still use gnu binutils cross compiled for i386-elf.
However, I don't use the compiling process described in an earlier post.
Instead, again, I use Homebrew.
First of all, to get a working cross target binutils, the brew formula
will have to be changed a bit
> brew edit binutils
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
> brew install binutils
and you're good to go.
As a bonus, binutils also contains usefull tools like objdump and readelf.
Qemu
----
Up until a few days ago, after compiling my kernel, I mounted an ext2
image using mac-fuse, copied the kernel binary into it, unmounted the
image and tested it using bochs.
Bochs has a really nice text based interface which works well as long as
the OS is only text based anyway, also a great debugger. Unfortunately,
you can't use both at once, but most of the time you don't need an
actual debugger to find the problems anyway.
The whole process can be streamlined through some shell scripting and I
had the overhead reduced to almost nothing, I thought...
Then I decided to take a look at [qemu](http://wiki.qemu.org/Main_Page)
again. I used it once before, but stopped due to the lack of a debugger
and text mode if I recall correctly. I knew back then that it could be
debugged with [gdb](http://www.gnu.org/software/gdb/), but I was on a
windows machine in those days, and... well... don't really want to talk
about it...
Anyway, I started having some problems with mac-fuse, so I thought
I should take a look at qemu again, since it has built in emulation
of a boot loader and can run a lone kernel binary passed to it as an
argument. So I started to look it up and it turned out to actually have
a text mode (curses mode).
So I went out on a whim and tried
> brew info qemu
By now you should know pretty much what I think of Homebrew, so the
results of that command pretty much sealed the deal.
Now I run my kernel in qemu through
> qemu-system-i386 -kernel kernel/kernel -curses
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
memory map. Further, this could be accessed using telnet from a
different tmux pane.
#!/bin/bash
tmux split-window -h 'qemu-system-i386 -kernel kernel/kernel -curses -monitor telnet:localhost:4444,server'
tmux select-pane -L
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
> brew tap homebrew/dupes
> brew edit gdb
Add the flag `--target=i386-elf` to the configure flags, save and
> brew install gdb
This will link to `ì386-elf-gdb` and can be run in yet another tmux window.
#!/bin/bash
tmux split-window -h 'qemu-system-i386 -kernel kernel/kernel -curses -monitor telnet:localhost:4444,server -s -S'
tmux select-pane -L
tmux slit-window -v 'i386-elf-gdb'
tmux select-pane -U
telnet localhost 4444
Upon start, gdb will look for a file called `.gdbinit` which in my case contains
file kernel/kernel
target remote localhost:1234
Results
-------
[![OSDEV build environment](/media/img/osdev_build_env.png)](/media/img/osdev_build_env_full.png)
Now that's neat!
The results can also be seen in git commit [0699c20381](https://github.com/thomasloven/os5/tree/0699c203818ec1d018c93b0192fba48ccb6879d8).

View File

@ -0,0 +1,127 @@
layout: post
title: "More Memory"
subtitle: "And processes"
tags: [osdev]
The kernel has been multithreaded for quite a while, so now it's time to
make it multiprocessing. Here's how I look at processes and threads in
single-processor systems.
###Thread
A thread or __thread of execution__ is the state of the processor at a
certain point. That is, the thread is made up of
- processor registers
- instruction pointer
- stack pointer
By saving those and replacing with other previously saved values,
threads can be switched in and out of execution. Each thread has its
own, unique stack.
###Process
A process is an isolated collection of threads. That is:
- A process has one or more threads.
- The threads in a process share memory space
- The threads in one process can not access the memory of the threads
in another process
There are some exceptions to the third point, but that's another show.
Each process has an associated memory translation map which can be
switched out to switch the active process. Generally, the current thread
will have to be changed at the same time since its code and data will be
in another memory space.
The kernel code and data resides in the upper part of memory, from
`0xC0000000` and above and is common to all memory translation maps.
That means that a kernel thread can be switched in without changing the
process, and also that the process can be switched freely while a kernel
thread is running.
###Synchronizing Memory Translation maps
The x86 memory management unit (MMU) governs how the processor accesses
memory. When paging is enabled any call to any address during code
execution is translated from a _virtual address_ to a _physical
address_. I usually call the set of translation rules a _memory
translation map_.
Each process has its own assigned memory translation map. Two processes'
memory translation maps may in unlikely cases map the same physical
memory pages to the same virtual addresses, but the maps are still
considered unique.
The mapping of the kernel memory space is equal in all maps. This means
that as long as the processor is executing code in the kernel space,
the active memory translation map may be changed without worrying about
corrupting data, since there's no difference between them. It does cause
a problem though. If something changes in the kernel space, the same
change must happen in all memory translation maps.
The way memory is translated makes the problem a little bit smaller.
Since the x86 family uses two-tier paging and, again, kernel space is
the same for all maps, we can use the same page tables for all maps.
That means we only need to consider changes in the page directory which
happens significantly less frequently (a naïve approximation: 1024 times
less often).
Still, when a change is made to the page directory which corresponds to
an address in kernel space, the change needs to be propagated to all
page directories in the system. There are a few ways this can be done:
- Keep track of all memory translation maps and update them all at the
same time when something happens to the kernel space.
- When a new memory translation map is switched in, copy any changes
from the previous one.
- Keep a master memory translation map and update that when a change is
made, then propagate the changes on request.
The first method sounds like a lot of work. Keeping track of the memory
translation maps isn't very hard - they all belong to a process, and we
need to keep track of all processes anyway. Updating them, however could
take a lot of time. As I write this, I have 164 processes currently
running on my computer. That's 164 memory translation maps that should
be updated, some of which may be currently paged out and written to
disk.
The second method sounds feasible. During the switch, we are probably
doing things to both memory translation maps anyway, so why not add this
step? It does add some overhead, but shouldn't be too bad.
The third method also sounds feasible. But how do we know when the
changed part is requested? The answer is page faults. If a page fault
occurs in kernel space and it turns out to be caused by a missing page
directory entry, check the master page directory. If there is an entry
there, add it to the current page directory and you're good to go.
Unfortunately, this means we can only add new page directory entries and
never change or delete them, because that wouldn't cause a page miss the
next time. Also, all the kernel page tables would need to be kept in
memory at all times and never be paged out. The memory hit isn't too bad
on a modern system, but it seems a bit inelegant now that I think about
it.
###What to use?
This brings up one of the points to why I'm keeping this blog. It makes
me think about stuff.
Before I did the reset described in [a previous
post](/blog/2013/02/A-Step-Back/) I used the second method above. When I
wrote the new code, I came up with the third method and thought I should
try it out. Now I'm starting to have second thoughts, though...
I'll give it some more thought and we'll see how it turns out.
###Next step
Again, I'm starting to ramble and the post is getting long and
embarrassingly unstructured so I'll cut it off here.
What I'll save for the next post is how to keep track of the memory
space for the processes.
###Usage
The methods described in this post has been implemented in git commit
[fa9e5929ce](https://github.com/thomasloven/os5/tree/fa9e5929ce6adaf62e6a85df284690b31163a4f9)

View File

@ -0,0 +1,55 @@
layout: post
title: "Two Small Projects"
subtitle: "Audio and Arduino"
###Breadboard Arduino
I didn't really like the arduino when I first heard about it. It
was vastly overpowered for any task I saw it used for and with the
interpreted coding it seemed little more than an expensive waste of
processor cycles.
Then I went and bought one anyway, and I am happy to admit that I was
wrong to think it useles. I remember opening the box, and ten minutes
later I had used it to solve a problem which had been bothering me for a
week. Now I'm thinking of getting a third one.
The one I use the most is an Arduino nano, which can be seen sitting on
my breadboard in this photo from a previous post
![Z80 tester wiring](/media/img/z80_tester.png)
This placement worked well, but I kept thinking about the big open area
just to the left of the Arduino...
So, one day I brought out the dremel and... perfect fit!
![Arduino breadboard](/media/img/mini_project_arduino_breadboard2.png)
To the sides are an IC holder I split in half and soldered on the back.
![Arduino soldering](/media/img/mini_project_arduino_breadboard1.png)
It was a bit tricky to avoid melting the plastics, but I think it turned
out allright.
Finally, heres a picture of it in use for studying the timings of an SD
card for my Z80 computer.
![Arduino in use](/media/img/mini_project_arduino_breadboard4.png)
###Mac mini headset adapter
I use a Mac mini (mid 2011) as my main desktop computer. I wanted
to connect a headset to it for skyping, but found out that the only
connections are Audio out and Line in.
The Audio out port, however, allows you to connect an
iPhone headset and use its microphone with the computer.
That gave me an idea, and after some searching I found
[this](http://benttronics.blogspot.se/2009/05/audio-breakout-cable-for-i
podiphone.html).
Once again I heated the soldering iron and got this:
![Headset adapter](/media/img/mini_project_headset_adapter.png)
Let me tell you, that four-pole connector is one tight fit.
The top two connectors are wired in parallell and allows me to have both
my speakers and my headset connected at the same time. It's easy enough
to just turn off the speakers when I don't need them and I wont ever
have to dig around behind the computer to connect the headset.

View File

@ -0,0 +1,189 @@
layout: post
title: "Even More Memory"
subtitle: "And processes again"
tags: [osdev]
I did say memory was an important part of what the operating system
does. Here's another post on it.
###Forking a process
By using a system call a process may __fork__ i.e. create a copy of
itself.
Right after the fork, the entire memory space should look the same to
both the parent and child. However, if one changes something, the change
should not affect the other.
For example:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char **argv)
{
int variable = 1;
int pid = fork();
int status;
if( pid )
{
// This is the parent
printf("Parent says: %d\n", variable);
variable = 2;
printf("Parent says: %d\n", variable);
waitpid(pid, &status, 0); // Let the child run
printf("Parent says: %d\n", variable);
} else {
// This is the child
printf("Child says: %d\n", variable);
variable = 3;
printf("Child says: %d\n", variable);
}
return 0;
}
{: .lang-c}
This simple program should output (assuming the parent is run first and
is not interrupted):
Parent says: 1
Parent says: 2
Child says: 1
Child says: 3
Parent says: 2
The virtual memory of the X86 architecture allows us to switch out the
entire memory space in one strike, and that allows for this behavior.
The naive way to implement this functionality is to make a copy of the
entire memory space, page for page, during the fork and assign it to the
child process. However, it is common for a new process to start its life
by performing `execve()` or similar and thus clearing or replacing its
entire memory space. Obviously this would make copying the memory space
just a waste of time.
A way to prevent this waste is to make all points on the two processes
memory maps point to the same physical memory as long as they only read.
A copy is made only if either process wishes to write to a memory area.
This method is known as _Copy on Write_.
Now, I never took any courses in data structures and I'm sure there's
better ways of doing this, but here's how I keep track of the memory
areas used and shared between processes.
###Process memory map
In the kernel, each process has an associated _memory map_. The memory
map contains information about the _memory areas_ of the process.
Each memory area describes a part of the process' memory. The size of
the part can go from `0` to the entire memory space. The memory area
structure thus has one field for the starting address of the area and
one for the end address. It also has a field describing the area type
and a flags field.
Each memory area belong in two doubly linked lists. One is the list of
memory areas belonging to a process. The other list is all copies of the
area.
Finally, each area has a pointer to its owning process.
Let's follow a memory area during part of a process' life.
###Setup
![PROCMM1](/media/img/procmm1.png){: .center .noborder}
In the figure above we see two processes, _A_ and _B_.
Let's say _A_ is the _init_ process and _B_ is a shell.
Currently, _A_ has four memory areas and _B_ has three. As you can see,
all the processes memory areas are linked together in a list (blue
arrows). _A_ and _B_ also share one area and the two representations are
linked together in another list (green arrows). The shared areas are
flagged as "Copy on Write" (red color).
We'll be following the rightmost memory area of process _B_. This area
represents the stack and stretches from `0xBFFFE000` to `0xC0000000`.
In other words, it is two memory pages long (assuming 4kb pages).
###Forking
The user types
> gcc hello_world.c
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
memory map for the new process. It then clones all memory areas into the
new map.
![PROCMM2](/media/img/procmm2.png){: .center .noborder}
The write flag of our area is unset and the CoW flag is set. The area is
then copied into the new map and the copies list is updated so that our
area and its copy can keep track of each other.
###Pushing to stack
Let's say the scheduler returns us to the child process (process _C_)
when the fork is complete.
The child process does some processing on the user entered command and
during this tries to write a value to the stack. Since the area
containing the stack is read-only this results in a page fault.
The page fault handler recognizes that the fault was caused by an
attempt to write to a read-only page and by a quick check finds out that
the area in question is marked for CoW, so it decides to make a copy of
it. There's no need to copy both pages, though, so the memory manager
first splits the area in two.
The same split is made in all copies of the same area. Finally, the area
we want is physically copied and write is enabled on it before control
is returned to the user process.
![PROCMM3](/media/img/procmm3.png){: .center .noborder}
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
the required area has no copies, so it is just set as read/write and
we're done.
![PROCMM4](/media/img/procmm4.png){: .center .noborder}
Actually, the parent process will probably perform a `waitpid` syscall
and sleep untill the child has finished, so let's go back to the child.
###Exiting
When the child process finishes, it frees all memory areas. When a page
marked CoW is requested, the first check performed by the kernel is
wether there actually are any other processes sharing the same area.
Otherwise, it just marks it as read/write and is done. Therefore, all
the child process needs to do is remove its own memory areas from the
list of copies and the parent will take care of the rest.
###Zero size areas
I said before that an area could have a zero size, i.e. the same start
and end address.
This is only useful in combination with a certain flag that allows the
area to grow automatically.
It could for example be used by a stack area which might originally
start at `0xC0000000` and end at `0xC0000000`. If an `uint32_t` is
pushed, the process will try to write to address `0xBFFFFFFC` which
results in a page fault.
The page fault handler will realize that theres a memory area right
above the address (say less than one page away) and that this area has
the autogrow flag enabled. It will then just expand the area and be
done.
The pros of this method is that we will never have to guess how large
the stack size should be. It will grow as neede (to an extent) or stay
at zero size if it's not needed.
###Git
The methods described in this post has been implemented in git commit
[cea5ec765f](https://github.com/thomasloven/os5/tree/cea5ec765ff683dbcf3
116006c43a195245d9d6e).

View File

@ -0,0 +1,119 @@
layout: post
title: "System calls"
subtitle: "Bend the stack to your will"
tags: [osdev]
System calls is the way user processes communicate to the kernel. Look
at the following program, for example.
#include <stdio.h>
int main(int argc, char **argv)
{
printf("Hello, world!");
return 0;
}
{: .lang-c}
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
then makes several more system calls before the `write()` and `exit()`
system calls represented by the two lines in the code.
System calls can be performed in several ways, but one of the most
common is through a special software interrupt with the `int`
instruction. For example, linux and most unix-like hobby kernels I've
studied use `int 0x80`. That's also what I chose to use in my kernel.
Next is the problem of passing data. The simplest way is using
registers, and that's what most projects seem to use. For this, I chose
a combination of a single register and the processes own stack.
###Sample system call
Let's look at how `read()` would be implemented. I've not actually
implemented it in my kernel yet, but here's how it would work.
####User side
First the definition in the c library:
int read(int file, char *ptr, int len)
{
return _syscall_read(file, ptr, len);
}
Simply a wrapper for an assembly function:
[global _syscall_read]
_syscall_read:
mov eax, SYSCALL_READ
int 0x80
mov [syscall_error], edx
ret
{: .lang-nasm}
This function puts an identifier for the system call in the `eax`
register and then execute the system call interrupt.
_Note:_ Here I return the error code through register
`edx`. In the actual code at this point, I used the
register `ebx`. I should have looked up [Calling
Conventions](http://wiki.osdev.org/Calling_Conventions) more carefully.
Of course, this can be simplified with a macro to
[global _syscall_read]
DEF_SYSCALL(read, SYSCALL_READ)
{: .lang-nasm}
####Kernel side
In the kernel, the system call is caught by the following function:
registers_t *syscall_handler(registers_t *r)
{
if(syscall_handlers[r->eax])
r = syscall_handlers[r->eax](r);
else
r->edx = ERR_NOSYSCALL;
return r;
}
If the system call is registered correctly in the kernel (through the
macro `KREG_SYSCALL(read, SYSCALL_READ)`), this will pass everything
onto the following function:
KDEF_SYSCALL(read, r)
{
process_stack stack = init_pstack();
r->eax = read((int)stack[0], (char *)stack[1], (int)stack[2]);
r->edx = errno;
return r;
}
The `init_pstack()` macro expands to `(unitptr_t *)(r->useresp + 0x4)`
and this lets us read the arguments passed to the system call from where
they are pushed on call.
Then the `read()` function has the same definition as the library version.
int read(int file, char *ptr, int len)
{
...
}
_Spoiler alert:_ Keeping a version of `read()` (and in fact every
syscall function) inside the kernel will turn out to have some really
cool advantages...
This works for c compiled with the `cdecl` calling convention. For other
languages or calling conventions, the asm functions will have to be
adjusted.
###Git
The methods described in this post has been implemented in git commit
[8a26e26163](https://github.com/thomasloven/os5/tree/8a26e26163c15c9d9854554dce9d4fc5ad8baee5).

View File

@ -0,0 +1,195 @@
layout: post
title: "Catching Up"
subtitle: "with a new toolchain"
tags: [osdev]
Well... I've been bad at updating the blog on my process again...
Last time I missed updating, I made a branch from an earlier git commit,
and threw away a bit of work and messy code rather than trying to catch
up.
This time, however, I'm actually quite happy that I didn't write too
much, because I've recently changed a lot of what I would have written
about.
So instead, I'll describe one of my most recent changes here - a new
cross compiler and a newlib port - and then in subsequent posts go over
chosen parts of the other things I've done.
So let's jump into it.
### Why a cross compiler
The be-or-not-to-be of cross compilers has been discussed at
length and it seems every time someone posts a description
on how to build one they are told that you don't need one if
you know how to use your compiler or whatever. For examples,
read
[the osdev wiki - Why do I need a Cross Compiler? talk page](http://wiki.osdev.org/Talk:Why_do_I_need_a_Cross_Compiler%3F)
(I don't recommend it if you're in a bad mood).
Well, I'm developing on OSX but have chosen to build an X86 kernel
in elf format due to the availability of documentation, support and
expertize. That's reason enough for a cross compiler build of binutils
at least.
Clang offers excellent support for cross compilation out of the box, so
I didn't use to have a need for a gcc cross compiler as illustrated in
[this post](/blog/2013/02/New-Environment/). As it turns out, however,
compiling newlib with that setup is rather annoying.
I'm also a fan of clean makefiles. Take a look at this:
VPATH := ../src
CC := i586-pc-myos-gcc
TARGETS := $(shell find ../src -name "*.c")
TARGETS := $(notdir $(TARGETS))
TARGETS := $(patsubst %.c, %, $(TARGETS))
all: $(TARGETS)
clean:
-rm $(TARGETS) 2>/dev/null
{: .lang-make}
That's the makefile for the entire `/bin` directory in my os.
So let's move on...
### Building newlib and gcc
For my toolchain build, I followed the tutorial at
[the osdev wiki](http://wiki.osdev.org/OS_Specific_Toolchain) with a few
changes. I chose to build everything inside the scope of
[Homebrew](http://brew.sh) to keep track of all files and eventually
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
name for my kernel besides an iteration number...) I did
export TARGET=i586-pc-myos
export PREFIX=/usr/local/Cellar/osdev/1.0
# Configure, build and install binutils
brew link osdev
# Configure, build and install gcc and libgcc
brew unlink osdev
brew link osdev
And that prepared me for building newlib.
### Building newlib
Now this was an adventure...
Newlib uses a directory structure that hasn't been supported by
automake and autoconf since a couple of versions. More specifically,
you need automake version 1.12 or earlier and autoconf version 2.64.
Unfortunately, those versions are not available through Homebrew, so ...
curl -O http://ftp.gnu.org/gnu/automake/automake-1.12.tar.gz
tar -zxf automake-1.12.tar.gz
mkdir -p build-automake
pushd build-automake
../automake-1.12/configure --prefix=/usr/local/Cellar/automake/1.12
make all -j
make install
popd
curl -O http://ftp.gnu.org/gnu/autoconf/autoconf-2.64.tar.gz
tar -zxf autoconf-2.64.tar.gz
pushd build-autoconf
../autoconf-2.64/configure --prefix=/usr/local/Cellar/autoconf/2.64
make all -j
make install
popd
brew switch automake 1.12
brew switch autoconf 2.64
Those last two lines tells Homebrew that you want to use those specific
versions for now.
Now for the neat part. I followed the wiki post and used the
[syscall interface](/blog/2013/06/System-Calls) i've described earlier
but I also wrapped `crt0.S` and `syscalls.c` in
#ifndef KERNEL_MODE
...
#endif
Then I built it all through
pushd build-newlib
../newlib/configure --target=$TARGET --prefix=$PREFIX
export CPPFLAGS_FOR_TARGET=-DKERNEL_MODE
make -j
make install
mv $PREFIX/$TARGET/lib/libc.a $PREFIX/$TARGET/lib/libkernel.a
rm -rf *
../newlib/configure --target=$TARGET --prefix=$PREFIX
export CPPFLAGS_FOR_TARGET=
make -j
make install
popd
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
kernel and allows me to use a standard C library with things like
`sprintf` and even a ready made `malloc`.
"But wait," you say. "Doesn't malloc make an sbrk() syscall? You can't
make a syscall from inside the kernel, can you?"
Well. Remember [my last post](/blog/2013/06/System-Calls) where I said
that keeping versions of every syscall function inside the kernel would
turn out to have some really cool advantages? Here they are.
I need an `sbrk` anyway, so all I have to do differently is make it
check whether the request came from a user process or from the kernel
and handle them a slight bit differently (i.e. make sure the `brk` is
above `0xC0000000` for kernel and below for user mode...).
Similar checks can be made for all syscalls and some doesn't actually
need any changing at all.
Needless to say, this revelation changed my kernel structure
quite a bit and I entirely threw away my own
[kernel heap manager](/blog/2012/07/Memory-Heap).
### Using this
Having the cross compiler toolchain and library setup allows me to
compile the kernel using a very simple makefile.
Somewhat simplified:
OBJECTS := $(shell find . -name "*.S")
OBJECTS += $(shell find . -name "*.c")
OBJECTS := $(OBJECTS:%.S=%.o)
OBJECTS := $(OBJECTS:%.c=%.o)
CC := i586-pc-myos-gcc
LDFLAGS := -nostdlib -T linkfile.ld
LDLIBS := -lkernel
kernel: $(OBJECTS)
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
and everything else is taken care of by the default rules of gnu make,
including preprocessing and assembling .S files.
For executables running under my operating system it's even easier
CC := i586-pc-myos-gcc
That's all.
### GAS
On a final note, the automatic rules for building object files out of .S
files runs them through `CC`, which means they are assembled by
`i586-pc-myos-as` which uses AT&T syntax rather than NASM which uses
Intel syntax. I actually converted all my assembly code to AT&T syntax,
but you might want to use the `.intel_syntax` directive instead.
### Git
The methods described in this post has been implemented in the directory
`toolchain` of git commit
[f09bd57b8e](https://github.com/thomasloven/os5/tree/f09bd57b8eb8c27607dab8250574c8eaed2939a0/toolchain).

View File

@ -0,0 +1,160 @@
layout: post
title: "Loading elf"
subtitle: "there's DWARF in my ELF."
tags: [osdev]
### Elf header format
Elf files all start with a header which identifies the file and explains
where to find everything. It has the following structure. The
[ELF specification](http://www.skyfree.org/linux/references/ELF_Format.pdf)
gives an excellent description on the meaning and use of each field.
typedef struct
{
uint8_t identity[16];
uint16_t type;
uint16_t machine;
uint32_t version;
uint32_t entry;
uint32_t ph_offset;
uint32_t sh_offset;
uint32_t flags;
uint16_t header_size;
uint16_t ph_size;
uint16_t ph_num;
uint16_t sh_size;
uint16_t sh_num;
uint16_t strtab_index;
}__attributes__((packed)) elf_header;
The first thing we should do is check whether we actually got an
executable ELF file. (In the following code, I'll assume the entire elf
file is located somewhere in memory and that this location is passed to
the `load_elf()` function.)
To check if the file is an ELF executable we can look at the
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`
field. For an executable standalone program, this should be `2`.
int load_elf(uint8_t *data)
{
elf_header *elf = (elf_header *)data;
if(is_elf(elf) != ELF_TYPE_EXECUTABLE)
return -1;
...
`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/).
int is_elf(elf_header *elf)
{
int iself = -1;
if((elf->identity[0] == 0x7f) && \
!strncmp((char *)&elf->identity[1], "ELF", 3))
{
iself = 0;
}
if(iself != -1)
iself = elf->type;
return iself;
}
Should be pretty straight forward. Let's continue.
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
the file.
typedef struct
{
uint32_t type;
uint32_t offset;
uint32_t virtual_address;
uint32_t physical_address;
uint32_t file_size;
uint32_t mem_size;
uint32_t flags;
uint32_t align;
}__attributes__((packed)) elf_phead;
The program headers each tell us about one section of the file, and we
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
looking for loadable sections and load them into memory.
...
elf_phead *phead = (elf_phead)&data[elf->ph_offset];
uint32_t i;
for(i = 0; i < elf->ph_num; i++)
{
if(phead[i].type == ELF_PT_LOAD)
{
load_elf_segment(data, &phead[i]);
}
}
return 0;
}
This would also be a good time to update the memory manager information
about the executable. You might want to keep track of the start and end
of code and data for example.
Anyway, `load_elf_segment()` looks like this
void load_elf_segment(uint8_t *data, elf_phead *phead)
{
uint32_t memsize = phead->mem_size; // Size in memory
uint32_t filesize = phead->file_size; // Size in file
uint32_t mempos = phead->virtual_address; // Offset in memory
uint32_t filepos = phead->offset; // Offset in file
uint32_t flags = MM_FLAG_READ;
if(phead->flags & ELF_PT_W) flags |= MM_FLAG_WRITE;
new_area(current->proc, mempos, mempos + memsize, \
flags, MM_TYPE_DATA);
if(memsize == 0) return;
memcpy(mempos, &data[filepos], filesize);
memset(mempos + filesize, 0, memsize - filesize);
}
Let's go through it.
First we define some helper variables.
Next we check if the section we're loading should be writable.
Then we request a new memory area from the [process memory
manager](/blog/2013/06/Even-More-Memory/).
Finally, we copy as much data as is provided in the file and fill the
rest of the new area with zeros.
And that's really all you need to do to load an ELF executable.
The only thing left is to jump to `elf->entry` and you're going.
### Improvements
Of course the entire executable image won't be loaded into memory in the
normal case, but it might be true for e.g. an `init` program or similar
that your bootloaded loads as a module to your kernel. Instead, you
should read the parts you want through your filesystem as you go along.
Or maybe you shouldn't. It doesn't make sense to load a huge program
into memory all at once. What if it encounters an error and exits with
99% of the code unexecuted?
Perhaps the process memory manager could be told where to find certain
parts of the program, and load them only when needed?
### Git
The methods described in this post has been implemented in git commit
[a4ca835d1d](https://github.com/thomasloven/os5/tree/a4ca835d1db61faf214b4b617d38a335ef05d142).

View File

@ -0,0 +1,94 @@
layout: post
title: "Virtual File System"
subtitle: "and a weird weird bug."
tags: [osdev]
I was planning to start this post with talking about how I was not very
satisfied with my VFS layer and how I would like to rewrite it. But then
I rewrote it instead... and ran into a really weird bug.
### The bug
I'll start with the bug, since it doesn't have anything to do with the
VFS, but perhaps someone could help me if I write it down.
So, I was happily coding away when I decided to run my emulator as
usual. Since I develop exclusively in a terminal environment right now
my setup for testing is four panes in tmux.
- One pane runs qemu and displays its output.
- One pane controls the qemu terminal through telnet.
- One pane displays the serial output of qemu.
- One pane runs gdb connected to qemu.
Qemu is setup to do nothing until I write 'c' in either the terminal or
gdb to allow me to setup breakpoints and such. Only this time, the qemu
pane closed immediately before I could do anything at all.
I tried again, with the same results. And again in a desperate display
of what Einstein allegedly called madness.
What was the problem? It obviously couldn't be anything in my code. My
code didn't even have time to start running!
After a while I reverted my recent changes anyway, and the testing setup
ran flawlessly.
What? But my code didn't even run!
I reapplied my changes one at a time and found the line that caused the
problem.
int i = 0;
What?? That's it? Declaring a variable?
Anyway. I changed my code about to do the same thing in another way.
Debugging worked like a charm, but there was some unexpected behavior.
The best way to nail it down seemed to be by printing a short string
whenever a function was run. And qemu crashed
What??? Does my emulator hate running working code?
Ok. I've had problems with string before. I ran objdump, commented out
the string and ran objdump again and compare the sections. They were
probably overlapping some page border and causing some problems I didn't
catch with my last linking script update. There really shouldn't be any
problems left, but anyone can be wrong. Nothing obvious, though... but when I ran the emulator again, it
crashed.
What???? But I commented the problem out. Didn't I?
Removing the commented line fixed the problem.
What????? A comment?
Next I tried running only qemu, without the other panes. It worked like
a clock. Starting the terminal console; no problem. Starting gdb...
crash! Hm...
Removing the `-ggdb` flag from my compiling worked. `-g1` worked `-g2`
did not...
I have a `.gdbinit` file in my code root directory, which makes gdb load
debug symbols and attach automatically. It also sets some useful debug
breakpoints, like whenever I encounter unsolvable page faults. It turns
out the breakpoints were the culprit. If I didn't set them, everything
ran fine.
Now I was getting somewhere. I thought.
After running both gdb and qemu through gdb and actually plowing through
the DWARF information from `objdump -w`. I finally gave up.
My conclusion:
This is either a bug in gcc, a bug in gdb or a bug in qemu. To me the
third alternative seems the likeliest by far.
My guess is that there's a but in qemus gdb stubs.
If you heard about this bug or experienced it yourself or just have any
guesses, please let me know.
### VFS
Oops.. this grew long... Let's take the VFS some other time, shall we?

View File

@ -0,0 +1,408 @@
layout: post
title: "Virtual File System 2"
subtitle: "for real this time."
tags: [osdev]
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
losses there, at least. Instead I've been busy with a special side
project that I will write a bit about later and a lot of personal stuff
that you probably don't care about if you're reading those posts. Unless
you're my wife, that is (I got married! :) ). Then again, if you're my
wife you're probably not reading those posts anyway, so I guess the
entire point of this paragraph was to tell you I got married(!).
Ahem...
###The virtual filesystem
So, as I said in [my last post](/blog/2013/08/Virtual-File-System/)
I recently rewrote my VFS layer. Although, I must admit I'm not quite satisfied
with it yet...
...
Know what? Let's rewrite it again!
###What I want
First of all: What do I want from the virtual filesystem?
The VFS should be an abstraction layer for the files used by the kernel
and user processes. The files in this case could be files on a disk,
the disk hardware itself, pipes, files stored in ram, read-only files
generated on-the-fly by the kernel, network connections(?) etc.
Further, I want the VFS to be independent of any disk file system, e.g.
the VFS shouldn't have user-group-other read-write-execute tuples just
because it's designed with ext2 in mind. It might have those tuples -
I haven't decided yet - but it it does it won't be because ext2 uses
them. Nor will the VFS be designed with ext2 or any other disk file
system in mind.
The VFS should offer the functions
open() // Open or create a file
close() // Close a file
read() // Read data from opened file
write() // Write data to opened file
move() // Move a file or directory
link() // Put a file or directory in path tree
unlink() // Remove a file or directory from path tree
stat() // Get more info about a file or directory
isatty() // Returns true if the file is a terminal
mkdir() // Create a directory
readdir() // Get a directory entry
finddir() // Find a file by name from a directory
for all files and directories regardless of their underlying device or
driver.
The file system should have a tree structure with a single root.
Filesystems should be mountable at any path in the tree provided the
path points to a directory or non-existing node within an existing
parent directory. I.e. if the empty directory `/foo`, a filesystem
can be mounted to `/foo` or `/foo/bar` but not to `foo/baz/bar`. If
`/foo` is not empty, it should a filesystem can still be mounted to
it, unless it is already a mountpoint. All contents of `/foo` are then
hidden untill the filesystem is unmounted. Mounting filesystems to
non-directories should not be possible. Mounted filesystems does not
have to have a root directory, but can consist of a single file.
Those are just some rules for mounting that I thought of pretty much
arbitrarily. I might change my mind later, but this will do for now.
###Implementation
The most important data structure of the VFS is the inode.
Each file used by the kernel gets an inode which keeps track of some
important information of it, such as what type of file it is or which
driver controls it.
Some inodes live in the mount tree, which keeps track of all mounted
filesystems. Looking up a file by absolute path always starts in the
mount tree and is performed by a function called namei (name to inode).
Example:
- A user process wants the file `/mnt/floppy/foo/bar.txt`
- `namei` starts at the VFS root /
- `namei` searches for `/mnt` in the VFS tree, finds it and gets it's inode.
- `namei` searches for `/mnt/floppy` in the VFS tree, finds it and gets it's inode.
- `namei` searches for `/mnt/floppy/foo`, which is not found.
- `namei` asks the `/mnt/floppy` inode for `/mnt/floppy/foo` and gets it's inode.
- `namei` asks the `/mnt/floppy/foo` inode for `/mnt/floppy/foo/bar.txt` and gets it's inode.
- `namei` returns the inode for `/mnt/floppy/foo/bar.txt`
A good starting point for the inode structure might be some pointers to
allow it to be placed in a tree, then.
struct vfs_node_st;
typedef vfs_node_t * INODE;
typedef struct vfs_node_st
{
char name[VFS_NAME_SZ];
INODE parent;
INODE child;
INODE older, younger;
uint32_t type;
} vfs_node_t;
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,
and it's far from the worst memory thief in this kernel anyway.
The `type` field in the VFS node struct is used by the VFS tree to make
sure stuff is only mounted onto directories.
I typedef `INODE` as a pointer to keep the code a bit cleaner and easier
to maintain.
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:
typedef struct vfs_driver_st
{
uint32_t (*open)(INODE, uint32_t);
uint32_t (*close)(INODE);
uint32_t (*read)(INODE, void *, uint32_t, uint32_t);
uint32_t (*write)(INODE, void *, uint32_t, uint32_t);
uint32_t (*link)(INODE, INODE, const char *);
uint32_t (*unlink)(INODE, const char *);
uint32_t (*stat)(INODE, struct stat *st);
uint32_t (*isatty)(INODE);
uint32_t (*mkdir)(INODE, const char *);
dirent_t *(*readdir)(INODE, uint32_t);
INODE (*finddir)(INODE, const char *);
} vfs_driver_t;
and add `vfs_driver_t *d` to the inode struct. I also added a length
value, a void pointer for arbitrary data used by the drivers and a flags
value - also for use by the drivers. The
inode struct now looks like this:
typedef struct vfs_node_st
{
char name[VFS_NAME_SZ];
void *parent;
void *child;
void *older, *younger;
uint32_t type;
vfs_driver_t *d;
void *data;
uint32_t flags;
uint32_t length;
}
###Vfs functions
Next, I create some wrapper functions to call the driver functions.
uint32_t vfs_open(INODE ino, uint32_t mode)
{
if(ino->d->open)
return ino->d->open(ino, mode);
return 0;
}
and similar for all functions except `readdir` and `finddir` which
contain code to handle `.` and `..` for mount roots.
dirent_t *vfs_readdir(INODE ino, uint32_t num)
{
if(ino->type & FS_MOUNT)
{
if(num == 0)
{
dirent_t *ret = calloc(1, sizeof(dirent_t));
ret->ino = ino;
strcpy(ret->name, ".");
return ret;
} else if(num == 1) {
dirent_t *ret = calloc(1, sizeof(dirent_t));
ret->ino = ino->parent;
strcpy(ret->name, "..");
return ret;
}
}
if(ino->d->readdir)
return ino->d->readdir(ino, num);
return 0;
}
&nbsp;
INODE vfs_finddir(INODE ino, const char *name)
{
if(ino->type & FS_MOUNT)
{
if(!strcmp(name, "."))
{
return ino;
} else if(!strcmp(name, "..")) {
return ino->parent;
}
}
if(ino->d->finddir)
return ino->d->finddir(ino, name);
if(ino->d->readdir)
{
// Backup solution
int num = 0;
dirent_t *de;
while(1)
{
de = vfs_readdir(ino, num);
if(!de)
return 0;
if(!strcmp(name, de->name))
break;
free(de->name);
free(de);
num++;
}
INODE ret = de->ino;
free(de->name);
free(de);
return ret;
}
return 0;
}
Finally, I needed a function for mounting filesystems in the mount tree
and the `namei` function, which can actually be combined since they both
need to traverse the entire path.
_Warning:_ Pointer-pointers ahead!
First: a function for traversing the mount tree as far as possible
INODE vfs_find_root(char **path)
{
// Find closest point in mount tree
INODE current = vfs_root;
INODE mount = current;
char *name;
while((name = strsep(path, "/")))
{
current = current->child;
while(current)
{
if(!strcmp(current->name, name))
{
mount = current;
break;
}
current = current->olderyounger;
}
if(!current)
{
if(*path)
{
*path = *path - 1;
*path[0] = '/';
}
*path = name;
break;
}
}
return (INODE)mount;
}
Pretty self explanatory. No? Well, `strsep` is a library function which
picks out one part of the path at a time and also advances the `path`
pointer. Then, for each part, we look through the children of the node
we're at for one with the right name. If it is not found, the path
pointer is backed up one step and the last node we found is returned.
The namei/mount function then uses this as a starting point:
INODE vfs_namei_mount(const char *path, INODE root)
{
char *npath = strdup(path);
char *pth = &npath[1];
// Find closest point in mount tree
INODE current = vfs_find_root(&pth);
char *name;
while(current && (name = strsep(&pth, "/")))
{
// Go through the path
INODE next = vfs_finddir(current, name);
if(root)
{
// If we want to mount someting
if(!next)
{
// Create last part of path if it doesn't exist
// But only if it is the last part.
if(pth)
return 0;
next = calloc(1, sizeof(vfs_node_t));
strcpy(next->name, name);
next->type = FS_DIRECTORY;
}
// Add path to mount tree
next->parent = current;
next->older = current->child;
current->child = next;
}
if(!next)
return 0;
if(!current->parent)
free(current);
current = next;
}
free(npath);
if(root && current->type == FS_DIRECTORY)
{
// Replace node in mount tree
root->parent = current->parent;
if(root->parent->child == current)
root->parent->child = root;
root->older = current->older;
if(root->older)
root->older->younger = current;
root->younger = current->younger;
if(root->younger)
root->younger->older = current;
strcpy(root->name, current->name);
root->type = FS_MOUNT;
if(current == vfs_root)
vfs_root = root;
free(current);
}
return current;
}
{: .lang-c}
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
until we reach the target or a dead end. If the dead end is at the very
last part of the path (`/foo/bar` in the example above) and we want to
mount something a new node is created. Otherwise the function returns.
Also, if the goal is to mount something, each part of the path is added
to the mount tree. Finally, the mounting is performed - if requested and
the final inode is returned.
I also made two simple wrappers for this function:
INODE vfs_namei(const char *path)
{
return vfs_namei_mount(path, 0);
}
INODE vfs_mount(const char *path, INODE root)
{
return vfs_namei_mount(path, root);
}
{: .lang-c}
And finally, a function for unmounting file systems:
INODE vfs_umount(const char *path)
{
char *npath = strdup(path);
char *pth = &npath[1];
INODE ino = vfs_find_root(&pth);
if(!ino || pth)
{
free(npath);
return 0;
}
if(ino->child)
{
free(npath);
return 0;
} else {
// Remove node from mount tree
if(ino->parent->child == ino)
ino->parent->child = ino->older;
if(ino->younger)
ino->younger->older = ino->older;
if(ino->older)
ino->older->younger = ino->younger;
free(npath);
return ino;
}
}
And that's it for now. A lot of code this time, but that's because I
don't want to push my changes to github quite yet, so I can't give you a
commit link.
Next time, I'll look at some file related system calls.

View File

@ -0,0 +1,196 @@
title: "VFS syscalls"
subtitle: "...so I put wrappers 'round your wrappers..."
tags: [osdev]
[Last time](/blog/2013/12/Virtual-File-System2/) I started to rewrite
the VFS layer of my hobby kernel - again. This time I'll take a look at
the system call couplings.
Since a [while](/blog/2013/08/Catching-Up/), I've had a cross compiler
and [newlib](http://sourceware.org/newlib) for my kernel, which means I
have some basic syscall interfaces to start from.
Newlib requires the following syscalls:
:::c
int close(int file)
int fstat(int file, struct stat *st)
int isatty(int file)
int link(char *old, char *new)
int lseek(int file, int ptr, int dir)
int open(const char *name, int flags, int mode)
int read(int file, char *ptr, int len)
int stat(const char *file, struct stat *st)
int unlink(char *name)
int write(int file, char *ptr, int len)
###open and close
Everything starts with `open`, so let's look at that first.
In order to keep track of the files that are opened by a process, we
need a new data structure, though; the _file descriptor_.
:::c
typedef struct
{
INODE ino;
uint32_t offset;
uint32_t flags;
uint32_t users;
} file_desc_t;
The file descriptor keeps track of our position in the file as well as
the mode it was opened in. File descriptors can also be shared between
processes (after a `fork()` for example), and it therefore has a use
counter. Two macros are used to manipulate the use counter
:::c
#define fd_get(fd) { (fd)->users++ }
#define fd_put(fd) { (fd)->users--; if(!(fd)->users)free(fd) }
Each process descriptor has an array of pointers to file descriptors
:::c
file_desc_t *fd[NUM_FILEDES];
`open` starts by finding a free file descriptor. It then finds the file,
opens the file and returns the index of the file descriptor it used:
:::c
int open(const char *name, int flags, int mode)
{
int fd;
// Find unused file descriptor
process_t *p = current->proc;
int i;
for(i=0; i < NUM_FILEDES; i++)
{
if(p->fd[i])
continue;
fd = i;
p->fd[fd] = calloc(1, sizeof(file_desc_t));
fd_get(p->fd[fd]);
break;
}
// Find file
INODE ino = vfs_namei(name);
// Open file
vfs_open(name, flags);
// Setup file descriptor
p->fd[fd]->ino = ino;
p->fd[fd]->offset = 0;
p->fd[fd]->flags = flags;
return fd;
}
I stripped away all of the sanity checking and error handling code here.
With that code, the function is more than twice as long.
`close` is even easier:
:::c
int close(int file)
{
int retval = vfs_close(p->fd[file]->ino);
if(!p->fd[file]->ino->parent)
free(p->fd[file]->ino);
fd_put(p->fd[file]);
p->fd[file] = 0;
return retval;
}
I always check if an inode has a parent before freeing it. If it has a
parent, it's part of the vfs mount tree, and should be kept around.
###read and write
Next, let's look at read.
It's actually really simple (excluding sanity checking and error
handling):
:::c
int read(int file, char *ptr, int len)
{
process_t *p = current->proc;
INODE node = p->fd[file]->ino;
int ret = vfs_read(node, ptr, len, p->fd[file]->offset);
p->fd[file]->offset += ret;
return ret;
}
Write is pretty much the same:
:::c
int write(int file, char *ptr, int len)
{
process_t *p = current->proc;
INODE node = p->fd[file]->ino;
int ret = vfs_write(node, ptr, len, p->fd[file]->offset);
p->fd[file]->offset += ret;
return ret;
}
###stat, fstat and isatty
`fstat` and `isatty` just passes on the information to the corresponding
vfs functions:
:::c
int fstat(int file, struct stat *st)
{
process_t *p = current->proc;
INODE node = p->fd[file]->ino;
return vfs_fstat(node, st);
}
:::c
int isatty(int file)
{
process_t *p = current->proc;
INODE node = p->fd[file]->ino;
return vfs_isatty(node);
}
`stat` performs a `namei` lookup to get the node instead of taking it
from the process' file descriptor table.
:::c
int stat(const char *file, struct stat *st)
{
INODE node = vfs_namei(file);
int retval = vfs_fstat(node, st);
if(!node->parent)
free(node);
return retval;
}
###lseek
The final function I'll look at now is `lseek` which sets the current
position in the file:
:::c
int lseek(int file, int ptr, int dir)
{
process_t *p = current->proc;
if(dir == SEEK_SET)
{
p->fd[file]->offset = ptr;
}
if(dir == SEEK_CUR)
{
p->fd[file]->offset += ptr;
}
if(dir == SEEK_END)
{
p->fd[file]->offset = p->fd[file]->ino->length + ptr;
}
return p->fd[file]->offset;
}
I'll leave `link` and `unlink` for now, and come back to them when
I need them (i.e. I wish to implement a user writeable filesystem).
In the next post, we'll mount an actual filesystem.

View File

@ -0,0 +1,95 @@
layout: post
title: "The debug file system"
subtitle: "It's still in the kernel!"
tags: [osdev]
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.
The filesystem I want to start with is what I call the `debug` file.
It is a single file, which mounts to `/` for now (later `/dev/debug`)
and cannot be read from. Writing to it will print the written text on
the screen.
Three functions are needed for newlib `fprintf` to be able to write to
it: `stat`, `isatty` and `write`. Those are all rather simple, since
they won't need to keep track of which file we're referencing.
uint32_t debug_stat(INODE node, struct stat *st)
{
memset(st, 0, sizeof(struct stat));
st->st_mode = S_IFCHR;
return 0;
}
I don't care much about the stat for the debug file. Maybe I'll add some
creation time or so later...
uint32_t debug_isatty(INODE node)
{
return 1;
}
The debug output is a terminal.
uint32_t debug_write(INODE node, void *buffer, uint32_t size, uint32_t offset)
{
char *buf = calloc(size + 1, 1);
memcpy(buf, buffer, size);
kdbg_puts(buf);
free(buf);
return size;
}
`kdbg_puts` is a function I wrote
[a long time ago](/blog/2012/06/Kernel-Debug-Functions/) which prints a
string to the screen. The first two lines are just to make sure that the
data is null-terminated.
With this, I can define a driver for the "debug device":
vfs_driver_t debug_driver =
{
0, // open
0, // close
0, // read
debug_write, // write
0, // link
0, // unlink
debug_stat, // stat
debug_isatty, // isatty
0, // mkdir
0, // readdir
0 // finddir
};
And then write a function to setup the device:
INODE debug_dev_init()
{
INODE node = calloc(1, sizeof(vfs_node_t));
strcpy(node->name, "debug");
node->d = &debug_driver;
node->type = FS_CHARDEV;
return node;
}
Then, to activate it, all I need to do is add
vfs_init();
vfs_mount("/", debug_dev_init());
in my kernel boot code. After that I can use the standard library
functions:
FILE *dbg = fopen("/", "w");
fprintf(dbg, "Hello, world!\n");
or even:
fopen("/", "r");
fopen("/", "w");
printf("Hello, world!\n");
That's it for this time. Next time, we'll do some piping!

185
pages/2013-12-18-Pipes.md Normal file
View File

@ -0,0 +1,185 @@
layout: post
title: "Pipes"
subtitle: "... and keyboard."
tags: [osdev]
In most unix-like systems, pipes can be used to make processes
communicate with each other. To the processes the pipe looks just like
any other file, but when one process tries to read from it, it will get
the data the other process wrote.
With a couple of hints from the people at #osdev on irc.freenode.net,
I finally got pipes to work - although a bit inefficiently - the way I
wanted a few days ago.
I implemented pipes as circular buffers, which makes them a perfect
example of the "producer-consumer problem" which is well known in
operating system theory. The schoolbook solution to the problem uses
sleeping processes in roughly the following way:
Producer/Writer:
- Write until all bytes are written or until the buffer is full.
- Wake any processes that are sleeping on the buffer.
- If you have more bytes to write, go to sleep on the buffer and then
repeat.
Consumer/Reader:
- Read until all bytes are read or until the buffer is empty.
- Wake any processes that are sleeping on the buffer.
- If there were no bytes to read, go to sleep on the buffer and then
repeat.
In code this translates to
uint32_t pipe_write(INODE ino, void *buffer, uint32_t size, uint32_t offset)
{
vfs_pipe_t *pipe = (vfs_pipe_t *)ino->data;
char *buf = (char *)buffer;
uint32_t bytes_written = 0;
while(bytes_written < size)
{
while((pipe->write_pos - pipe->read_pos) < pipe->size && bytes_written < size)
{
pipe->buffer[pipe->write_pos % pipe->size] = buf[bytes_written];
bytes_written++;
pipe->write_pos++;
}
scheduler_wake(&pipe->waiting);
if(bytes_written < size)
{
scheduler_sleep(current, &pipe->waiting);
schedule();
}
}
return bytes_written;
}
uint32_t pipe_read(INODE ino, void *buffer, uint32_t size, uint32_t offset)
{
vfs_pipe_t *pipe = (vfs_pipe_t *)ino->data;
char *buf = (char *)buffer;
uint32_t bytes_read = 0;
while(bytes_read == 0)
{
while((pipe->write_pos - pipe->read_pos) > 0 && bytes_read < size)
{
buf[bytes_read] = pipe->buffer[pipe->read_pos % pipe->size];
bytes_read++;
pipe->read_pos++;
}
scheduler_wake(&pipe->waiting);
if(bytes_read == 0)
{
scheduler_sleep(current, &pipe->waiting);
schedule();
}
}
return bytes_read;
}
Of course there should also be:
- Sanity checking.
- A lock to prevent reading and writing at the same time.
- Handling of the cases where there are no readers or no writers.
Note that the reader only blocks if no bytes at all have been read,
while the writer blocks if not all bytes have been written.
Creating a new pipe is a bit special, though.
An ordinary pipe has two ends, one for writing and one for reading. The
best way I found to implement this is to have the pipe represented by
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
inodes point to the same pipe struct.
typedef struct vfs_pipe
{
char *buffer;
uint32_t size;
uint32_t read_pos;
uint32_t write_pos;
uint32_t readers;
uint32_t writers;
semaphore_t semaphore;
list_head_t waiting;
} vfs_pipe_t;
The `readers` and `writers` fields are incremented or decremented when the read or
write end respectively are opened or closed... respectively...
Creating a new pipe (somewhat simplified):
uint32_t new_pipe(uint32_t size, INODE *nodes)
{
vfs_pipe_t *pipe = calloc(1, sizeof(vfs_pipe_t));
pipe->buffer = malloc(size);
pipe->size = size;
nodes[0] = calloc(1, sizeof(vfs_node_t));
nodes[0]->d = &pipe_driver;
nodes[0]->data = pipe;
nodes[0]->flags = PIPE_READ;
nodes[1] = calloc(1, sizeof(vfs_node_t));
nodes[1]->d = &pipe_driver;
nodes[1]->data = pipe;
nodes[1]->flags = PIPE_WRITE;
return 0;
}
###Using pipes
When starting up the keyboard driver, a pipe is created and connected to
`stdin` - the first file descriptor - of the current process.
void keyboard_init()
{
INODE tmp[2];
new_pipe(1024, tmp);
keyboard_pipe = tmp[1];
vfs_open(keyboard_pipe, O_WRONLY);
process_t *p = current->proc;
p->fd[0] = calloc(1, sizeof(file_desc_t));
fd_get(p->fd[0]);
p->fd[0]->ino = tmp[0];
p->fd[0]->flags = O_RDONLY;
vfs_open(tmp[0], O_RDONLY);
register_int_handler(IRQ2INT(IRQ_KBD), keyboard_handler);
}
The keyboard handler (based off
[Brandon Friesen's tutorial](http://www.osdever.net/bkerndev/Docs/keyboard.htm)
writes each decoded key to the pipe:
registers_t *keyboard_handler(registers_t *r)
{
char code[2];
[...]
while(inb(KBD_STATUS_PORT) & 0x2);
scancode = inb(KBD_DATA_PORT);
code[0] = keyboard_decode(scancode);
code[1] = '\0';
if(code[0])
{
vfs_write(keyboard_pipe, (char *)code, 1, 0);
}
[...]
}
At a later stage, I'll probably make the keyboard driver in the kernel
just pass the scancodes to the pipe and have the decoding done by
a terminal process which in turn pipes the decoded keys on to the shell,
and so on...
Next step is to add an actual filesystem! Of sorts...

View File

@ -0,0 +1,224 @@
layout: post
title: "TAR Filesystem"
subtitle: "Almost a useful system"
tags: [osdev]
It's finally time to implement an actuall filesystem, and all the hard
work with the VFS framework will pay off.
For my first filesystem, I chose the tape archive format - also known as
a tarball. It's a bit like what
[James Molloy](http://www.jamesmolloy.co.uk/tutorial_html/8.-The%20VFS%20and%20the%20initrd.html)
did but with support for directories.
###Tar file format
The tar file format - as the name implies - was designed for storing
files on magnetic tape, which can only be read and written sequentially.
Therefore, there is no index of the files in the archive, but each file
is preceded by a data block in human-readable format.
In the POSIX standard of 1988, the data block has the following format:
typedef struct
{
unsigned char name[100];
unsigned char mode[8];
unsigned char uid[8];
unsigned char gid[8];
unsigned char size[12];
unsigned char mtime[12];
unsigned char checksum[8];
unsigned char type[1];
unsigned char linkname[100];
unsigned char tar_indicator[6];
unsigned char tar_version[2];
unsigned char owner[32];
unsigned char group[32];
unsigned char device_major[8];
unsigned char device_minor[8];
unsigned char prefix[155];
}__attribute__((packed)) tar_header_t;
The data block is 500 bytes long, but the data starts 512 (one standard
disk sector) bytes after the start of the data block
The data itself is also padded so that the next data block starts at
a 512 byte boundary.
For historical reasons all numerical values (i.e. `uid`, `gid`, `size`,
`mtime`, `checksum`, `device_major` and `device_minor`) are in __octal__
ascii format (ascii `'0'` to `'7'`) padded with leading zeroes and
terminated with a `null` character (`0x00`) or space (`0x20`).
###Reshaping the tree
The TAR format is very good for storing data on tape where read and
write operations are sequential, but it makes less sense once the files
are loaded into ram and you might want to access them randomly.
Instead, I reshape the file list into a file tree:
tree_t *build_tar_tree(tar_header_t *tar)
{
...
while(tar->name[0])
{
tartree_add_node(tree, tar, (char *)&tar->name);
uint32_t size;
sscanf((char *)&tar->size, "%o", &size);
tar = (tar_header_t *)((size_t)tar + size + 512);
if((size_t)tar % 512)
tar = (tar_header_t *)((uint32_t)tar + 512 - ((uint32_t)tar%512))
}
...
}
void tartree_add_node(tree_t *tree, tar_header_t *tar, char *path)
{
...
tree_node_t *node = tree->root;
char *p;
for(p = strtok(path, "/"); p; p = strtok(NULL, "/"))
{
int found = 0;
list_t *l;
for_each_in_list(&node->children)
{
...
if(!strcmp(entry->name, p))
{
found = 1;
node = tn;
break;
}
}
if(!found)
{
...
tarfs_entry_t *n = malloc(sizeof(tar_entry_t));
n->name = strdup(p);
n->tar = tar;
...
tree_make_child(node, new);
node = new;
}
}
}
Note that this assumes that the files and directories of the tar archive
are in top-down order, e.g. that `/bin` is before `/bin/echo`, otherwise
strange things happen. Deterministic and expected things, mind you, but
strange.
###Mounting the filesystem
The tarfs driver makes use of the data field in the vfs node to store
the tar tree.
INODE tarfs_init(tar_header_t *tar)
{
vfs_node_t *node = calloc(1, sizeof(vfs_node_t));
strcpy(node->name, "tarfs");
node->d = &tarfs_driver;
node->type = FS_DIRECTORY;
tree_t *tar_tree = build_tar_tree(tar);
node->data = tar_tree->root;
free(tar_tree);
return node;
}
I then add the following to my kernel initialization function:
tar_header_t *tarfs_location = assert_higher((tar_header_t *)mods[0].mod_start);
vfs_mount("/", tarfs_init(tarfs_location));
where `mods` is the multiboot modules table as loaded by grub.
###The tarfs driver
Finally, the tarfs driver functions. For now I only need to implement
`read()` and `finddir()`.
INODE tar_finddir(INODE dir, const char *name)
{
tree_node_t *tn = (tree_node_t *)dir->data;
list_t *l;
for_each_in_list(&tn->children, l)
{
tree_node_t *cn = list_entry(l, tree_node_t, siblings);
...
if(!strcmp(entry->name, name)
{
INODE node = calloc(1, sizeof(vfs_node_t));
strcpy(node->name, entry->name);
node->d = &tarfs_driver;
node->data = (void *)cn;
sscanf((char *)&entry->tar->size, "%o", &node->length);
if(entry->tar->type[0] == TAR_TYPE_DIR)
node->type = FS_DIRECTORY;
else
node->type = FS_FILE;
return node;
}
}
return 0;
}
`Finddir` allocates space for a new inode for each file that is searched
for. It's up to the caller to free the inode when it's done with it.
This should be true for all `finddir` functions, but I think I've missed
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
it in the `data` field of the inode.
uint32_t read_tar(INODE node, void *buffer, uint32_t size, uint32_t offset)
{
tree_node_t *tn = (tree_node_t *)node->data;
tarfs_entry_t *te = (tarfs_entry_t *)tn->item;
tar_header_t *tar = te->tar;
uint32_t tsz;
sscanf((char *)&tar->size, "%o", &tsz);
if(offset > tsz) return EOF;
if((size + offset) > tsz) size = tsz - offset;
offset = offset + (uint32_t)tar + 512;
memcpy(buffer, (void *)offset, size);
if(size == tsz - offset)
((char *)buffer)[size] = EOF;
return size;
}
###Using it
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
$ tar -cf tarfs.tar tarfs/*
and then make sure `tarfs.tar` is loaded as a multiboot module by qemu
$ qemu-system-i386 -kernel kernel -initrd tarfs.tar
or by adding a line to the grub `menu.lst` file.
module /boot/tarfs.tar
###Final note
While writing this post, I got back to polishing this code and added
`readdir` for the tarfs. I also added buffering of inodes, so that if
the same file is asked for twice, the same inode is returned. This meant
I had to add a `flush` function to the driver structure.
I also rewrote a bit of both `vfs_finddir` and `vfs_readdir`. This in
turn forced me to go back on a design decision. The VFS can now only
mount new devices onto already existing paths (excluding `/`).
Just in case I'd write something in coming posts that might confuse
you...
### Git
I finally decided to push upstream, so here's the latest commit:
[843520405e](https://github.com/thomasloven/os5/tree/843520405eb950413c75b2ca2a0ee638f15e7f4e).
It still contains some stuff I haven't written about, though.

View File

@ -0,0 +1,265 @@
layout: post
title: "Process Syscalls"
subtitle: "Almost caught up..."
tags: [osdev]
I've described my [syscall interface](/blog/2013/06/System-Calls/) previously. I've also described
the [file-related syscalls](/blog/2013/12/VFS-syscalls/). In order to build [newlib](http://wiki.osdev.org/Porting_Newlib), some more
syscalls are required.
Those are:
void *sbrk(int incr);
int getpid();
int fork();
void _exit(int rc);
int wait(int *status);
int kill(int pid, int sig);
int execve(char *name, char **argv, char **env);
Let's just go through them one at a time:
###sbrk
`sbrk` is a bit special, since it actually has two versions - one for
kernel use and one for user space processes.
The user space one makes use of the [process memory
manager](/blog/2013/06/Even-More-Memory/) to return
a chunk of new memory for the `malloc` functions.
void *usr_sbrk(int incr)
{
process_t *p = current->proc;
mem_area_t *area = find_including(p, p->mm.data_end);
if(area)
{
if(area->end > (p->mm.data_end + incr))
{
// The current memory area is large enough
} else {
// Increase memory area
new_area(p, area->end, p->mm.data_end + incr, \
MM_FLAG_READ | MM_FLAG_WRITE | MM_FLAG_CANSHARE, \
MM_TYPE_DATA);
}
} else {
// Create a new memory area
new-area(p, p->mm.data_end, p->mm.data_end + incr, \
MM_FLAG_READ | MM_FLAG_WRITE | MM_FLAG_CANSHARE, \
MM_TYPE_DATA);
}
p->mm.data_end = p->mm.data_end + incr;
return (void *)(p->mm.data_end - incr);
}
The kernel space version is just a simple linear allocator
uintptr_t kmem_top = KERNEL_HEAP_START;
uintptr_t kmem_ptr = KERNEL_HEAP_START;
void *sbrk(int incr)
{
if(kmem_ptr + incr > KERNEL_HEAP_END)
{
// PANIC!
...
}
while(kmem_top < kmem_ptr + incr)
{
vmm_page_set(kmem_top, vmm_page_val(pmm_alloc_page(), \
PAGE_PRESENT | PAGE_WRITE));
kmem_top += PAGE_SIZE;
}
kmem_ptr = kmem_ptr + incr;
return (void *)kmem_ptr - incr;
}
Hopefully it's obvious why the kernel one is called `sbrk` while the
user one has a different name.
###getpid
`getpid` is rather obvious:
int getpid()
{
return current->proc->pid;
}
###fork
`fork` clones the current process and starts a new thread of execution.
int fork()
{
process_t *child = fork_process();
thread_t *ch_thread = list_entry(child->threads.next, thread_t, process_threads);
ch_thread->r.eax = 0;
scheduler_insert(ch_thread);
return child->pid;
}
###_exit
`_exit` stops a program and wakes up any processes that are sleeping on
it.
void _exit(int rc)
{
process_t *p = current->proc;
// Close all open files
int i;
for(i = 0; i < NUM_FILEDES; i++)
{
if(p->fd[i])
close(i);
}
exit_process(current->proc, rc);
current->state = THREAD_STATE_FINISHED;
schedule();
}
`_exit` doesn't return, and in fact `schedule()` will never return as
far as this thread is concerned.
Note that the process still exists. It is not completely destroyed until
its parent process has executed a `wait` syscall.
###wait
Actually, I didn't quite implement `wait` yet, but instead use
a `waitpid` for now, which is a bit more specific:
int waitpid(int pid)
{
process_t *proc = get_process(pid);
while(proc->state != PROC_STATE_FINISHED)
{
scheduler_sleep(current, &proc->waiting);
schedule();
}
int ret = proc->exit_code;
free_process(proc);
return ret;
}
This _should_ contain a check that process `pid` is a child of the
current process too...
###kill
I'll let `kill` wait for now. My next post will probably be on signals,
so it'll fit better there anyway.
###execve
Now, here's the big stuff.
`execve` launches new programs from the filesystem, so what it has to do
is:
- Find the correct executable
- Save the arguments
- Save the environmental variables
- Free the user memory space
- Load the executable
- Prepare a new user stack
- Restore the arguments and environment variables
First of all, the executable is found. If it doesn't exist, we want to
fail as early as possible - before we destroy everything.
int execve(char *name, char **argv, char **env)
{
INODE executable = vfs_namei(name);
if(!executable)
{
errno = ENOENT;
return -1;
}
...
The arguments and environment are null-terminated lists of strings
stored in user space, so they have to be copied into kernel space before
the user space is destroyed:
...
usigned int envc = 0;
char **temp_env = 0;
if(env)
{
while(env[envc++]); // Count number of environmental variables
temp_env = calloc(envc, sizeof(char *));
unsigned int i = 0;
while(env[i])
{
temp_env[i] = strdup(env[i]);
i++;
}
}
// Do the same thing for argv
...
Next, Delete all memory from the previous executable and [load the new
one](/blog/2013/08/Loading-Elf/):
procmm_removeall(current->proc);
load_elf(executable);
current->r.eax = current->r.ebx = current->r.ecx = \
current->r.edx = 0;
We need to put the arguments and environment back into the new
executable's user space, so a new stack area is created:
new_area(current->proc, USER_STACK_TOP, USER_STACK_TOP, \
MM_FLAG_WRITE | MM_FLAG_GROWSDOWN | MM_FLAG_ADDONUSE, \
MM_TYPE_STACK);
current->kernel_thread = (registers_t *)current;
uint32_t *pos = (uint32_t *)USER_STACK_TOP;
Then, copy the environment and arguments onto the stack:
if(env)
{
pos = pos - envc*sizeof(char *)/sizeof(uint32_t) - 1;
env = (char **)pos;
int i = 0;
while(temp_env[i])
{
pos = pos - strlen(temp_env[i])/sizeof(uint32_t) - 2;
memcpy(pos, temp_env[i], strlen(temp_env[i])+1);
env[i] = (char *)pos;
i++;
}
env[envc-1] = 0;
}
// Do the same for argc
...
And finally, push the argument count, argument list and environment list
onto the stack:
pos = pos - 3;
pos[0] = (uint32_t)argc - 1;
pos[1] = (uint32_t)argv;
pos[2] = (uint32_t)env;
current->r.useresp = current->r.ebp = (uint32_t)pos;
current->r.ecx = (uint32_t)pos;
return 0;
}
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
pushes `ecx` to the stack and then calls `_init` which looks like this:
extern char **environ;
void _init(uint32_t *args)
{
int argc;
char **argv;
if(args)
{
argc = args[0];
argv = (char **)args[1];
environ = (char **)args[2];
} else {...}
exit(main(argc, argv));
}

180
pages/2014-01-30-Signals.md Normal file
View File

@ -0,0 +1,180 @@
layout: post
title: "Signals"
subtitle: "Another rewrite"
tags: [osdev]
Time for another live rewrite - or at least a restructuring.
###Signals in newlib
Newlib has a kind of signals built in without any help from the kernel.
Those signals can't be sent between processes, though, so I'd like to
implement my own.
I'll just look at `signal` and `kill` and ignore things like
`sigaction` for now.
First of all, in order to compile newlib with kernel supported signals,
you need the line
newlib_cflags="${newlib_cflags} -DSIGNAL_PROVIDED"
in your entry in `newlib/configure.host` as described in [the osdev
wiki](http://wiki.osdev.org/OS_Specific_Toolchain#Signal_handling).
Then you need the syscalls:
sig_t signal(int signum, sig_t handler);
int kill(int pid, int sig);
###Raising signals
The [posix
specification](http://pubs.opengroup.org/onlinepubs/9699919799/)
contains a lot of rules about how signals should behave in a number of
situations. I probably got a lot of them wrong, but I'll try to fix them
as I go along later.
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`
typedef struct
{
uint32_t sig;
uint32_t sender;
list_head_t queue;
} signal_t;
is created and added to the processes list:
int signal_process(int pid, int signum)
{
process_t *p = get_process(pid);
...
signal_t *signal = calloc(1, sizeof(signal_t));
signal->sig = signum;
signal->sender = current->proc->pid;
init_list(signal->queue);
append_to_list(p->signal_queue, signal->queue);
if(p == current->proc)
{
handle_signals(current);
}
return 0;
}
If the currently running thread is in the process being signalled, we
handle the signals immediately. Otherwise it can wait for a bit.
`signal_process` is called by the `kill` syscall.
###The signal syscall
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`
syscall calls the following function:
sig_t switch_handler(int signum, sig_t handler)
{
...
sig_t old = current->proc->signal_handler[signum];
current->proc->signal_handler[signum] = handler;
return old;
}
The cut out part of this function contains code to make sure the handler
of signal 9 (`SIGKILL`) is never changed from `SIG_DFL`.
###Handling signals
Signals should be handled as soon as possible, though there's no reason
to let the receiving process skip the line in the scheduler. So as soon
as the process continues running should be soon enough.
I chose to hook into the my interrupt handler:
registers_t *idt_handler(registers_t *r)
{
...
if(int_handlers[r->int_no)
{
...
registers_t *ret = int_handlers[r->int_no](r);
if((ret->cs & 0x3) == 0x3)
{
ret = (registers_t *)handle_signals((thread_t *)ret);
...
}
...
return ret
}
...
}
So, what does `handle_signals` actually do?
It start by finding the signal queue of the process to which the passed
thread belongs and steps through it one entry at a time. If a signal is
queued and not blocked (actual blocking is not implemented yet) it
checks what the handler for that signal number is. The handler may be
`SIG_IGN`, `SIG_DFL` or a pointer to a function in userspace.
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
following:
void sighandler_ignore(int num)
{
(void)num;
}
void sighandler_terminate(int num)
{
fprintf(stderr,, "Process %x terminated by signal %x\n", \
current->proc->pid, num);
_exit(num);
}
void sighandler_coredump(int num)
{ /* Same as above */ }
void sighandler_stop(int num)
{ /* Not implemented yet */ }
void sighandler_continue(int num)
{ /* Not implemented yet */ }
If the handler is a function a new thread is created to handle it:
...
sig_t handler = th->proc->signal_handler[signal->sig];
thread_t *h = new_thread((void (*)(void))handler, 1);
append_to_list(th->proc->threads, h->process_threads);
h->proc = th->proc;
uint32_t *stack = ((uint32_t *)th->r.useresp;
*--stack = signal->sig;
*--stack = SIGNAL_RETURN_ADDRESS;
h->r.useresp = h->r.ebp = (uint32_t)stack;
remove_from_list(signal->queue);
free(signal);
scheduler_remove(h);
scheduler_sleep(th, &h->waiting);
scheduler_cheat(h);
schedule();
}
This creates a new thread and pushes the signal number (as an argument)
and a return address to the threads stack. It then places the new thread
at the beginning of the schedulers queue and switches to it.
`SIGNAL_RETURN_ADDRESS` is chosen to cause a page fault when the signal
handler returns. The page fault handler catches this and recognizes the
address so that the thread can be safely destroyed.
Now the page fault handler can also be setup to send a `SIGSEGV` signal
when a userspace thread page faults.
###Code
Git commit
[1ec132c](https://github.com/thomasloven/os5/tree/1ec132ce60ba627e522019d456e0cec993193153)

View File

@ -0,0 +1,116 @@
layout: post
title: "Debug Printing"
subtitle: "Running out of space"
tags: [osdev]
I still haven't quite caught up with all the stuff I wanted to write
about [back in August](/blog/2013/08/Catching-Up/), but at some point
when I started to write little userspace programs that wrote stuff to
the screen, I thought my kernel debug messages started to get in the
way.
I'm still not ready to give up debug messages from the kernel, so the
best alternative seemed to be to output them to something else, such as
a serial port.
Writing to a serial port [is rather
easy](http://wiki.osdev.org/Serial_Ports), so there's no need for me to
go into detail there. I made a few changes to my emulation setup,
though, that made showing the debug messages a bit nicer.
###Tmux
I've described how I use tmux with qemu, gdb and telnet
[before](/blog/2013/02/New-Environment/). It was easy to add another
pane which displays the debug information, but one thing at a time.
Here's what I did in a bash script:
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
`tarfs.tar` as a multiboot module. The `-monitor` flag starts a telnet
server for controlling the emulator. `-s` starts a gdb server at tcp
port 1234 and `-S` makes the emulator wait for a command before it
starts running the kernel. Finally, the `-serial` flag saves all output
from serial port 1 to a file.
The next step is:
tmux split-window -v 'tail -f serial.out | util/colorize.sh'
which opens up another tmux pane that displays the serial output using
the auto-updating feature of `tail`. The script `colorize.sh` colorizes
certain words of the output:
#!/usr/bin/env bash
C_NO=`echo -e "\\033[0m"`
C_RED=`echo -e "\\033[31m"`
C_GREEN=`echo -e "\\033[32m"`
C_YELLOW=`echo -e "\\033[33m"`
C_BLUE=`echo -e "\\033[36m"`
while read line; do
echo $line | sed \
-e "s/\(\[info\]\)/${C_BLUE}\1$[C_NO}/" \
-e "s/\(\[status\]\)/${C_GREEN}\1$[C_NO}/" \
-e "s/\(\[warning\]\)/${C_YELLOW}\1$[C_NO}/" \
-e "s/\(\[error\]\)/${C_RED}\1$[C_NO}/"
done
Next is
tmux select-pane -L
tmux split-window -v 'i586-elf-gdb'
tmux select-pane -U
telnet localhost 4444
Which opens a new pane for the `gdb` debugger and then moves back to the
first pane to open the telnet terminal.
[![Debug
Printing](/media/img/debug_print.png)](/media/img/debug_print_full.png)
###Git
Notice the yellow lines in that screenshot?
The ones that say
Kernel git data: [5e6074a (dirty)] Fri Mar 7 13:45:31 2014 +0100
(HEAD, harddrive): Ext2 unlink function. Untested.
Kernel compilation: Mar 7 2014 14:06:27
The data for this is stored in a special file called `version.c`
#include <version.h>
char * __kernel_git_hash = GITHASH;
char * __kernel_git_date = GITDATE;
int __kernel_git_dirty = GITDIRTY;
char * __kernel_git_message = GITMESSAGE;
char * __kernel_git_branch = GITBRANCH;
char * __kernel_build_date = __DATE__;
char * __kernel_build_time = __TIME__;
which has a special line in the kernel makefile:
version.o: CFLAGS += -DGITHASH='$(shell git log -1 --pretty="tformat:%h")' \
-DGITDATE='$(shell git log -1 --pretty="tformat:%cd")' \
-DGITDIRTY='$(shell [[ -n `git status -s 2> /dev/null` ]] && echo 1 || echo 0)' \
-DGITMESSATE='$(shell git log -1 --pretty="tformat:%s")' \
-DGITBRANCH='$(shell git log -1 --pretty="tformat:%d")'
A few more lines makes sure `version.c` is always recompiled if any
other part of the kernel is:
kernel: $(kernel_objects)
rm -f version.o
$(MAKE) version.o
$(LINK)
Obviously, this is a bit simplified. But not much. I might make a post
about my makefiles some day...
But with this, I've kind of caught up. Neat! Back to programming ahead
of myself!

View File

@ -0,0 +1,169 @@
layout: post
title: "DITo - Framework"
subtitle: "the Disk Image TOols"
tags: [osdev]
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
territory for me. In fact, my only experience from disks and filesystems
were from when I got started in osdeving and found some tutorial in pdf
form which described how to write a bootloader in asm that read a kernel
from a FAT12 floppy disk.
Since then, whenever I needed a disk image for testing, I'd go through
a painful process of finding an image with GRUB preinstalled, mounting
it using a discontinued third party application, copy stuff to it, hope
I would be able to unmount it without the entire computer freezing up
and finally pray that it worked when I started the emulator. In short,
trying to manage a disk image from the command line in OSX sucks.
That's when I realized I could kill two birds with one stone. By writing
a tool for managing files in a disk image without mounting it, I could
gain understanding and experience of working with filesystems. If I
wrote it well, I would probably be able to reuse much of the code for
my kernel as well. At the time I had just finished my master thesis and
had all but signed the contract for my current employment, so I had some
free time on my hands while the paperwork fell through.
The result was [DITo - Disk Image
Tools](https://github.com/thomasloven/dito), a c library and set of
applications for creating and handling disk images from the command
line.
Recently, I actually did copy some of the code from DITo into my kernel.
Immagine my surprise when it actually worked like a charm after changing
only a few function calls.
I've since realized a couple of mistakes though, and decided to rewrite
some parts from scratch. Let's go!
###Drive operations
The basic operations of DITo are reading from or writing to image files
or disk drives. Each drive type has a driver
typedef struct drive_driver
{
int (*open)(struct drive_t *d, int flags);
int (*close)(struct drive_t *d, int flags);
int (*read)(struct drive_t *d, void *buffer, size_t length, off_t offset);
int (*write)(struct drive_t *d, void *buffer, size_t length, off_t offset);
} drive_driver_t;
The drive type contains a pointer to the driver and a pointer to some
arbitrary data used by the driver.
typedef struct drive_t
{
struct drive_driver *d;
void *data;
} drive_t;
Then there are some wrapper functions for performing the required
operations:
int drive_open(struct drive_t *d, int flags)
{
if(d->d->open)
return d->d->open(d, flags);
else
return 0;
}
and simmilar for `drive_close`, `drive_read` and `drive_write`.
###Filesystem operations
The next important part of DITo is the filesystem handling. After
thinking about it, the important primitive functions for all file
operations I could think about are all in a filesystem driver struct:
struct fs_driver
{
INODE (*open)(struct fs_t *fs, const char *path, int flags);
int (*close)(struct fs_t *fs, INODE ino);
int (*read)(struct fs_t *fs, INODE ino, void *buffer, size_t length, off_t offset);
int (*write)(struct fs_t *fs, INODE ino, void *buffer, size_t length, off_t offset);
int (*truncate)(struct fs_t *fs, INODE ino, off_t length);
int (*stat)(struct fs_t *fs, INODE ino, struct stat *st);
int (*touch)(struct fs_t *fs, const char *path, struct stat *st);
int (*link)(struct fs_t *fs, const char *path1, const char *path2);
int (*unlink)(struct fs_t *fs, const char *path);
dirent_t *(*readdir)(struct fs_t *fs, INODE dir, unsigned int num);
};
The `fs_t` type contains a pointer to the driver, a pointer to the drive
and a general data pointer.
typedef struct fs_t
{
struct fs_driver *driver;
drive_t *d;
void *data;
} fs_t;
The wrapper functions `fs_open`, `fs_close` and so on work the same way
as the `drive_*` functions.
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.
struct ino_st
{
fs_t *fs;
unsigned int ino;
void *data;
};
typedef struct ino_st * INODE;
And that's the basic framework. As you probably notice, the same `fs_t`
pointer is passed to most functions twice. Once as `fs` and once as
`ino->fs`. I decided to keep it this way to get the function interface
consistant, and also for the possible sanity check `fs == ino->fs`.
The idea behind the framework is that the same functions should be
usable for all kinds of filesystems on all kinds of drives. For example,
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
someting like this:
drive_t *fat_disk = image_drive("floppy.img");
drive_open(fat_disk, READ_FLAG);
drive_t *ext2_disk = image_drive("harddrive.img");
drive_open(ext2_disk, READ_WRITE_FLAG);
drive_t *ext2_partition = mbr_drive(ext2_disk, 2);
drive_open(ext2_partition, READ_WRITE_FLAG);
fs_t *fat = fat_fs(fat_disk);
fs_t *ext2 = ext2_fs(ext2_partition);
INODE source = fs_open(fat, "/path/to/file", READ_FLAG);
struct st *st = malloc(sizeof(struct st));
fs_struct(fat, source, st);
fs_touch(ext2, "/new/path", st);
INODE destination = fs_open(ext2, "/new/path", WRITE_FLAG);
void *buffer = malloc(BUFER_SIZE);
off_t offset = 0;
off_t add = 0;
while(add = fs_read(fat, source, buffer, BUFFER_SIZE, offset))
{
fs_write(ext2, destination, buffer, BUFFER_SIZE, offset);
offset += add;
}
fs_close(destination);
fs_close(source);
drive_close(ext2_partition);
drive_close(fat_disk);
Which of couse will eventually become its own tool so that the actual
work the end user has to do becomes:
$ dito-cp floppy.img:/path/to/file harddrive.img:2:/new/path

View File

@ -0,0 +1,126 @@
layout: post
title: "DITo - Drives"
subtitle: "Exploring the MBR"
tags: [osdev]
Let's write a few drive drivers.
### Pure image
The simplest image we'd have to handle is a plain image file, such as
a floppy disk. There's only a single partition and a one-to-one mapping
from the image to the drive.
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
hate to lose that...
struct im_data
{
char *filename;
FILE *file;
};
The functions required for the driver would then make use of the
c standard library:
int im_open(drive_t *d, int flags)
{
struct im_data *data = d->data;
data->file = fopen(data->filename, flags);
return 1;
}
int im_close(drive_t *d, int flags)
{
struct im_data *data = d->data;
fclose(data->file);
return 1;
}
int im_read(drive_t *d, void *buffer, size_t length, off_t offset)
{
struct im_data *data = d->data;
fseek(data->file, offset, SEEK_SET);
return fread(buffer, length, 1, data->file);
}
int im_write(drive_t *d, void *buffer, size_t length, off_t offset)
{
struct im_data *data = d->data;
fseek(data->file, offset, SEEK_SET);
return fwrite(buffer, length, 1, data->file);
}
The function for setting up the drive is equally simple:
drive_driver_t im_driver =
{
im_open,
im_close,
im_read,
im_write,
};
drive_t *image_drive(const char *filename)
{
drive_t *d = calloc(1, sizeof(drive_t));
struct im_data *data = calloc(1, sizeof(struct im_data));
d->driver = &im_driver;
d->data = data;
data->filename = strndup(filename, FILENAME_MAX_LENGTH);
return d;
}
As always, I've stripped away all sanity checking and error handling.
###MBR formated disks
In order to use a harddrive for different things (like file storage or
swap space), it can be split into several partitions. One standard for
doing this is through a table in the MBR or Master Boot Record of the
disk.
The MBR starts with 436 bytes of space for a bootloader, then there's a
10 byte disk ID followed by 4 partition table entries.
The entries of the table has the following structure:
struct MBR
{
uint8_t boot_indicator;
uint8_t start_chs[3];
uint8_t system_id;
uint8_t end_chs[3];
uint32_t start_lba;
uint32_t num_sectors;
}__attribute__((packed));
_CHS_ and _LBA_ are different ways of addressing sectors of the disk.
CHS - or Cylinder Sector Head - addresses the sectors by their physical
location on the disk. The format in this table is first 8 bytes for
the wanted head (two heads per harddrive platter), then 6 bits for the
sector (angle on the platter) and finally 10 bits for the cylinder
(distance from platter center). This method requires knowledge of the
actual physical structure of the drive and maxes out at around 8 GB with
512 byte sectors.
LBA - or Logical Block Addressing - leaves all that to the drive and
addresses the sectors by a single number. With 512 byte sectors, this is
useful up to about 2 GB.
For our purposes, the fields `start_lba` and `num_sectors` are all we
need (and `system_id` to check if the partition table entry is used).
###MBR driver
The driver for reading from a partition is pretty alike the plain image
driver. Principally, the only difference is that 512 times `start_lba`
has to be added to the offset for reading and writing, and that care
must be taken not to allow reading or writing outside of the partition
boundaries.
I even wrote the driver to piggyback on a different driver. I.e. the
function for creating the drive object requires a drive object as
argument (as in the [example in the previous
post](/blog/2014/04/Dito-Framework/)).

View File

@ -0,0 +1,8 @@
layout: post
title: "DITo - Ext2"
subtitle: "A beginning..."
tags: [osdev]
It's finally time to start looking at an actual filesystem.
I chose to begin with ext2.

13
pages/about-page.md Normal file
View File

@ -0,0 +1,13 @@
title: About this page
permalink: /about/this-page/
I'm not really a ruby kind of guy, but this page runs on
[Jekyll](http://jekyllrb.com/) and [Sass.](http://sass-lang.com/) anyway.
The color scheme is based on
[Solarized](http://ethanschoonover.com/solarized) and the font is
[DejaVu Serif](http://dejavu-fonts.org).
Code snippets are displayed using
[google-code-prettify](http://code.google.com/p/google-code-prettify/) with
some custom syntax files by me.

39
pages/about.md Normal file
View File

@ -0,0 +1,39 @@
title: About me
permalink: /about/
Thomas Lovén
------------
<img src="/media/img/thomas.png" width="300" class="right">
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
studying.
I'm currently 26, newly wed and back in Karlskrona.
On my free time I play clarinet or tinker with some electronics or
programming project.
Education
---------
I have a masters degree in engineering physics with a Complex Adaptive
Systems specialisation from Chalmers University of Technology in
Gothenburg.
[Complex Adaptive Systems](http://studycas.com) focuses on describing
and analyzing complex or stochastic systems as well as adaptive
problem solving methods. Some courses I have read are Neural Networks,
Biologically Inspired Optimization Algorithms, Dynamical Systems and
Computational Biology.
[Further details](/about/education/)
Résumé
------
Outdated. Will return after update.
About the page
--------------
[Further details](/about/this-page/)

79
pages/education.md Normal file
View File

@ -0,0 +1,79 @@
title: About me - Education details
permalink: /about/education/
Teknisk fysik
-------------
Teknisk fysik - Engineering physics - is a bachelors programme at Chalmers University of Technology.
Courses are held in Swedish.
Translation and grades can be provided on request.
I earned my bachelors degree in 2012.
TDA305 Datorintroduktion 1,5hp
ESS115 Elektriska nät och system 7,5hp
EEF031 Elektromagnetisk fältteori 7,5hp
TIF080 Experimentell fysik 1 - mätteknik 9hp
TIF090 Experimentell fysik 2 - bas 4,5hp
FFY011 Fasta tillståndets fysik 7,5hp
MVE035 Flervariabelanalys 6hp
MVE030 Fourieranalys 6hp
TIF010 Fysiken omkring oss 7,5hp
TMA970 Inledande matematisk analys 6hp
FKA150 Inledande teknisk kommunikation 1,5hp
TIFX02 Kandidatarbete vid Teknisk fysik 15hp
MVE025 Komplex matematisk analys 6hp
FUF040 Kvantfysik 6hp
TMA660 Linjär algebra och geometri 4,5hp
TMA671 Linjär algebra och numerisk analys 7,5hp
TMA976 Matematisk analys, fortsättning 6hp
TMA321 Matematisk statistik 4,5hp
FFM515 Mekanik 1 7,5hp
FFM520 Mekanik 2 6hp
TIF075 Miljöfysik 4,5hp
FFY091 Optik 6hp
TIN211 Programmeringsteknik 6hp
ERE091 Reglerteknik 4,5hp
FUF045 Speciell relativitetsteori 4,5hp
TME055 Strömningsmekanik 4,5hp
FUF050 Subatomär fysik 6hp
FTF140 Termodynamik och statistisk fysik 7,5hp
TIF100 Tillämpad kvantfysik 4,5hp
FFM232 Vektorfält och klassisk fysik 4,5hp
{: .prettyprint .lang-betyg}
###Bachelors thesis
For our bachelors thesis me, two of my fellow students at Engineering
physics and three students at Elektroteknik - roughly electrics and
electronics - developed, built and tested a wireless charing station for
cellular phones.
Complex Adaptive Systems
------------------------
Complex Adaptive Systems is a masters programme at Chalmers University of Technology.
Courses are held in English
Grades can be provided on request.
As of January 2013 I have just begun work on my Masters thesis.
TIN092 Algorithms 7,5hp
FFR135 Artificial neural networks 7,5hp
FFR125 Autonomous agents 7,5hp
FFR141 Complex systems seminar 7,5hp
FFR110 Computational biology 1 7,5hp
TDA351 Cryptography 7,5hp
TIF115 Dynamical systems 7,5hp
TIF160 Humanoid robotics 7,5hp
RRY025 Image processing 7,5hp
MVE370 Matematik och samhälle 7,5hp
FFR120 Simulation of complex systems 7,5hp
FFR105 Stochastic optimization algorithms 7,5hp
MVE080 Vetenskaplig visualisering 7,5hp
{: .prettyprint .lang-betyg}
__Matematik och samhälle__ translates to __Mathematics and society__.
__Vetenskaplig visualisering__ translate to __Scientific visualization__.

14
requirements.txt Normal file
View File

@ -0,0 +1,14 @@
arrow==0.8.0
click==6.6
Flask==0.11.1
Flask-FlatPages==0.6
Frozen-Flask==0.13
itsdangerous==0.24
Jinja2==2.8
Markdown==2.6.7
MarkupSafe==0.23
Pygments==2.1.3
python-dateutil==2.5.3
PyYAML==3.12
six==1.10.0
Werkzeug==0.11.11

75
sitebuilder.py Executable file
View File

@ -0,0 +1,75 @@
#!/usr/bin/env python
import sys
from os.path import basename
from glob import glob
from flask import Flask, render_template, send_from_directory
from flask_flatpages import FlatPages
from flask_frozen import Freezer
import arrow
DEBUG = True
FLATPAGES_AUTO_RELOAD = DEBUG
FLATPAGES_EXTENSION = '.md'
FLATPAGES_ROOT = 'pages'
app = Flask(__name__)
app.config.from_object(__name__)
pages = FlatPages(app)
freezer = Freezer(app)
permalinks = {}
blogposts = []
tags = set()
for page in pages:
parts = page.path.split('-')
if page.meta.get('permalink', None):
page.meta['url'] = page.meta['permalink']
else:
page.meta['url'] = '/blog/{}/{}/{}/'.format(parts[0], parts[1], '-'.join(parts[3:]))
date = arrow.get('-'.join(parts[0:3]))
page.meta['date'] = date
page.meta['datestr'] = date.format('D MMM YYYY')
blogposts.append(page)
tags.update(page.meta.get('tags', []))
permalinks[page.meta['url']] = page
blogposts = sorted(blogposts, key=lambda p: p.meta['date'], reverse=True)
# Static files
@app.route("/src/<path:path>")
def scripts(path):
return send_from_directory('src', path)
@app.route("/media/<path:path>")
def media(path):
return send_from_directory('media', path)
@app.route("/favicon.ico")
def favicon():
return send_from_directory('src', 'favicon.ico')
@app.route("/")
def index():
return "Hello World!"
@app.route("/<path:path>")
def permalink(path):
page = permalinks['/'+path]
if path.startswith('blog/'):
return render_template('post.html', page=page)
return render_template('page.html', page=page)
@app.route("/blog/")
def blog_default():
return render_template('blog.html', posts=blogposts, tags=tags)
@app.route("/blog/<string:tag>/")
def tag(tag):
posts = [post for post in blogposts if tag in post.meta.get('tags', [])]
return render_template('blog.html', posts=posts, title=tag, tags=tags)
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "build":
freezer.freeze()
else:
app.run()

BIN
src/Averia-Regular.ttf Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

19
src/blankwin.js Normal file
View File

@ -0,0 +1,19 @@
this.blankwin = function(){
var hostname = window.location.hostname;
hostname = hostname.replace("www","").toLowerCase();
var a = document.getElementsByTagName("a");
this.check = function(obj){
var href = obj.href.toLowerCase();
return ((href.indexOf("http://")!=-1 || href.indexOf("https://")!=-1) && href.indexOf(hostname)==-1) ? true : false;
};
this.set = function(obj){
obj.target = "_blank";
obj.className = "external";
};
for (var i=0; i<a.length; i++) {
if(check(a[i])) set(a[i]);
};
};

96
src/codestyle.css Normal file
View File

@ -0,0 +1,96 @@
/* Text */
.codehilite .t { color: #586e75 }
/* Whitespace */
.codehilite .w { color: #073642 }
/* Error */
.codehilite .err { color: #cb4b16; }
/* Keyword */
.codehilite .k { color: #859900 }
.codehilite .kc { color: #2aa198 } /* Keyword.Constant */
.codehilite .kd { color: #268bd2 } /* Keyword.Declaration */
.codehilite .kn { color: #b58900 } /* Keyword.Namespace */
.codehilite .kp { color: #859900 } /* Keyword.Pseudo */
.codehilite .kr { color: #073642 } /* Keyword.Reserved */
.codehilite .kt { color: #b58900 } /* Keyword.Type */
/* Name */
.codehilite .n { color: #586e75 }
.codehilite .na { color: #2aa198 } /* Name.Attribute */
.codehilite .nb { color: #268bd2 } /* Name.Builtin */
.codehilite .nc { color: #268bd2 } /* Name.Class */
.codehilite .ne { color: #cb4b16 } /* Name.Error */
.codehilite .no { color: #2aa198 } /* Name.Constant */
.codehilite .nd { color: #2aa198 } /* Name.Decorator */
.codehilite .ni { color: #2aa198; font-weight: bold } /* Name.Entity */
.codehilite .nf { color: #268bd2 } /* Name.Function */
.codehilite .nn { color: #586e75; } /* Name.Namespace */
.codehilite .nt { color: #2aa198; font-weight: bold } /* Name.Tag */
.codehilite .nv { color: #cb4b16 } /* Name.Variable */
/* Builtin */
.codehilite .b { color: #859900 }
.codehilite .bp { color: #586e75 } /* Name.Builtin.Pseudo */
/* Variable */
.codehilite .v { color: #586e75 }
.codehilite .vc { color: #586e75 } /* Name.Variable.Class */
.codehilite .vg { color: #268bd2 } /* Name.Variable.Global */
.codehilite .vi { color: #268bd2 } /* Name.Variable.Instance */
/* Literal */
/* Literal.Number */
.codehilite .m { color: #268bd2 } /* Literal.Number */
.codehilite .mf { color: #268bd2 } /* Literal.Number.Float */
.codehilite .mh { color: #268bd2 } /* Literal.Number.Hex */
.codehilite .mi { color: #268bd2 } /* Literal.Number.Integer */
.codehilite .mo { color: #268bd2 } /* Literal.Number.Oct */
/* Literal.String */
.codehilite .s { color: #2aa198 }
.codehilite .sb { color: #2aa198 } /* Literal.String.Backtick */
.codehilite .sc { color: #2aa198 } /* Literal.String.Char */
.codehilite .sd { color: #2aa198 } /* Literal.String.Doc */
.codehilite .s2 { color: #2aa198 } /* Literal.String.Double */
.codehilite .se { color: #cb4b16 } /* Literal.String.Escape */
.codehilite .sh { color: #2aa198 } /* Literal.String.Heredoc */
.codehilite .si { color: #cb4b16 } /* Literal.String.Interpol */
.codehilite .sx { color: #2aa198 } /* Literal.String.Other */
.codehilite .sr { color: #cb4b16 } /* Literal.String.Regex */
.codehilite .s1 { color: #2aa198 } /* Literal.String.Single */
.codehilite .ss { color: #cb4b16 } /* Literal.String.Symbol */
/* Literal.Integer */
.codehilite .il { color: #268bd2 } /* Literal.Number.Integer.Long */
/* Operator */
.codehilite .o { color: #586e75 }
.codehilite .ow { color: #859900 } /* Operator.Word */
/* Punctuation */
.codehilite .p { color: #586e75 }
/* Comment */
.codehilite .c { color: #93a1a1; font-style: italic }
.codehilite .cm { color: #93a1a1; } /* Comment.Multiline */
.codehilite .cp { color: #93a1a1 } /* Comment.Preproc */
.codehilite .c1 { color: #93a1a1; } /* Comment.Single */
.codehilite .cs { color: #93a1a1; } /* Comment.Special */
.codehilite .hll { background-color: #dc322f }
/* Generic */
.codehilite .g { color: #586e75 }
.codehilite .gd { color: #586e75 } /* Generic.Deleted */
.codehilite .ge { font-style: italic } /* Generic.Emph */
.codehilite .gr { color: #586e75 } /* Generic.Error */
.codehilite .gh { color: #586e75; font-weight: bold } /* Generic.Heading */
.codehilite .gi { color: #586e75 } /* Generic.Inserted */
.codehilite .go { color: #586e75 } /* Generic.Output */
.codehilite .gp { color: #586e75 } /* Generic.Prompt */
.codehilite .gs { font-weight: 586e75 } /* Generic.Strong */
.codehilite .gu { color: #586e75; font-weight: bold } /* Generic.Subheading */
.codehilite .gt { color: #586e75 } /* Generic.Traceback */

1498
src/cufon-yui.js Normal file

File diff suppressed because it is too large Load Diff

7
src/cufon-yui2.js Normal file

File diff suppressed because one or more lines are too long

34
src/desert.css Normal file
View File

@ -0,0 +1,34 @@
* desert scheme ported from vim to google prettify */
pre { display: block; background-color: #333 }
pre .nocode { background-color: none; color: #000 }
pre .str { color: #ffa0a0 } /* string - pink */
pre .kwd { color: #f0e68c; font-weight: bold }
pre .com { color: #87ceeb } /* comment - skyblue */
pre .typ { color: #98fb98 } /* type - lightgreen */
pre .lit { color: #cd5c5c } /* literal - darkred */
pre .pun { color: #fff } /* punctuation */
pre .pln { color: #fff } /* plaintext */
pre .tag { color: #f0e68c; font-weight: bold } /* html/xml tag - lightyellow */
pre .atn { color: #bdb76b; font-weight: bold } /* attribute name - khaki */
pre .atv { color: #ffa0a0 } /* attribute value - pink */
pre .dec { color: #98fb98 } /* decimal - lightgreen */
/* Specify class=linenums on a pre to get line numbering */
ol.linenums { margin-top: 0; margin-bottom: 0; color: #AEAEAE } /* IE indents via margin-left */
li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 { list-style-type: none }
/* Alternate shading for lines */
li.L1,li.L3,li.L5,li.L7,li.L9 { }
@media print {
pre { background-color: none }
pre .str, code .str { color: #060 }
pre .kwd, code .kwd { color: #006; font-weight: bold }
pre .com, code .com { color: #600; font-style: italic }
pre .typ, code .typ { color: #404; font-weight: bold }
pre .lit, code .lit { color: #044 }
pre .pun, code .pun { color: #440 }
pre .pln, code .pln { color: #000 }
pre .tag, code .tag { color: #006; font-weight: bold }
pre .atn, code .atn { color: #404 }
pre .atv, code .atv { color: #060 }
}

BIN
src/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,364 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="specimen_files/easytabs.js" type="text/javascript" charset="utf-8"></script>
<link rel="stylesheet" href="specimen_files/specimen_stylesheet.css" type="text/css" charset="utf-8" />
<link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8" />
<style type="text/css">
body{
font-family: 'dejavu_serifbook';
}
</style>
<title>DejaVu Serif Book Specimen</title>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
$('#container').easyTabs({defaultContent:1});
});
</script>
</head>
<body>
<div id="container">
<div id="header">
DejaVu Serif Book </div>
<ul class="tabs">
<li><a href="#specimen">Specimen</a></li>
<li><a href="#layout">Sample Layout</a></li>
<li><a href="#installing">Installing Webfonts</a></li>
</ul>
<div id="main_content">
<div id="specimen">
<div class="section">
<div class="grid12 firstcol">
<div class="huge">AaBb</div>
</div>
</div>
<div class="section">
<div class="glyph_range">A&#x200B;B&#x200b;C&#x200b;D&#x200b;E&#x200b;F&#x200b;G&#x200b;H&#x200b;I&#x200b;J&#x200b;K&#x200b;L&#x200b;M&#x200b;N&#x200b;O&#x200b;P&#x200b;Q&#x200b;R&#x200b;S&#x200b;T&#x200b;U&#x200b;V&#x200b;W&#x200b;X&#x200b;Y&#x200b;Z&#x200b;a&#x200b;b&#x200b;c&#x200b;d&#x200b;e&#x200b;f&#x200b;g&#x200b;h&#x200b;i&#x200b;j&#x200b;k&#x200b;l&#x200b;m&#x200b;n&#x200b;o&#x200b;p&#x200b;q&#x200b;r&#x200b;s&#x200b;t&#x200b;u&#x200b;v&#x200b;w&#x200b;x&#x200b;y&#x200b;z&#x200b;1&#x200b;2&#x200b;3&#x200b;4&#x200b;5&#x200b;6&#x200b;7&#x200b;8&#x200b;9&#x200b;0&#x200b;&amp;&#x200b;.&#x200b;,&#x200b;?&#x200b;!&#x200b;&#64;&#x200b;(&#x200b;)&#x200b;#&#x200b;$&#x200b;%&#x200b;*&#x200b;+&#x200b;-&#x200b;=&#x200b;:&#x200b;;</div>
</div>
<div class="section">
<div class="grid12 firstcol">
<table class="sample_table">
<tr><td>10</td><td class="size10">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>11</td><td class="size11">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>12</td><td class="size12">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>13</td><td class="size13">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>14</td><td class="size14">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>16</td><td class="size16">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>18</td><td class="size18">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>20</td><td class="size20">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>24</td><td class="size24">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>30</td><td class="size30">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>36</td><td class="size36">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>48</td><td class="size48">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>60</td><td class="size60">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>72</td><td class="size72">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>90</td><td class="size90">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
</table>
</div>
</div>
<div class="section" id="bodycomparison">
<div id="xheight">
<div class="fontbody">&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;body</div><div class="arialbody">body</div><div class="verdanabody">body</div><div class="georgiabody">body</div></div>
<div class="fontbody" style="z-index:1">
body<span>DejaVu Serif Book</span>
</div>
<div class="arialbody" style="z-index:1">
body<span>Arial</span>
</div>
<div class="verdanabody" style="z-index:1">
body<span>Verdana</span>
</div>
<div class="georgiabody" style="z-index:1">
body<span>Georgia</span>
</div>
</div>
<div class="section psample psample_row1" id="">
<div class="grid2 firstcol">
<p class="size10"><span>10.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid3">
<p class="size11"><span>11.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid3">
<p class="size12"><span>12.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid4">
<p class="size13"><span>13.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="white_blend"></div>
</div>
<div class="section psample psample_row2" id="">
<div class="grid3 firstcol">
<p class="size14"><span>14.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid4">
<p class="size16"><span>16.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid5">
<p class="size18"><span>18.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="white_blend"></div>
</div>
<div class="section psample psample_row3" id="">
<div class="grid5 firstcol">
<p class="size20"><span>20.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid7">
<p class="size24"><span>24.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="white_blend"></div>
</div>
<div class="section psample psample_row4" id="">
<div class="grid12 firstcol">
<p class="size30"><span>30.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="white_blend"></div>
</div>
<div class="section psample psample_row1 fullreverse">
<div class="grid2 firstcol">
<p class="size10"><span>10.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid3">
<p class="size11"><span>11.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid3">
<p class="size12"><span>12.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid4">
<p class="size13"><span>13.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="black_blend"></div>
</div>
<div class="section psample psample_row2 fullreverse">
<div class="grid3 firstcol">
<p class="size14"><span>14.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid4">
<p class="size16"><span>16.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid5">
<p class="size18"><span>18.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="black_blend"></div>
</div>
<div class="section psample fullreverse psample_row3" id="">
<div class="grid5 firstcol">
<p class="size20"><span>20.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid7">
<p class="size24"><span>24.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="black_blend"></div>
</div>
<div class="section psample fullreverse psample_row4" id="" style="border-bottom: 20px #000 solid;">
<div class="grid12 firstcol">
<p class="size30"><span>30.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="black_blend"></div>
</div>
</div>
<div id="layout">
<div class="section">
<div class="grid12 firstcol">
<h1>Lorem Ipsum Dolor</h1>
<h2>Etiam porta sem malesuada magna mollis euismod</h2>
<p class="byline">By <a href="#link">Aenean Lacinia</a></p>
</div>
</div>
<div class="section">
<div class="grid8 firstcol">
<p class="large">Donec sed odio dui. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. </p>
<h3>Pellentesque ornare sem</h3>
<p>Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit. </p>
<p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. </p>
<p>Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Aenean lacinia bibendum nulla sed consectetur. </p>
<p>Nullam quis risus eget urna mollis ornare vel eu leo. Nullam quis risus eget urna mollis ornare vel eu leo. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec ullamcorper nulla non metus auctor fringilla. </p>
<h3>Cras mattis consectetur</h3>
<p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean lacinia bibendum nulla sed consectetur. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cras mattis consectetur purus sit amet fermentum. </p>
<p>Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo. Cras mattis consectetur purus sit amet fermentum.</p>
</div>
<div class="grid4 sidebar">
<div class="box reverse">
<p class="last">Nullam quis risus eget urna mollis ornare vel eu leo. Donec ullamcorper nulla non metus auctor fringilla. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p>
</div>
<p class="caption">Maecenas sed diam eget risus varius.</p>
<p>Vestibulum id ligula porta felis euismod semper. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Maecenas sed diam eget risus varius blandit sit amet non magna. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. </p>
<p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Aenean lacinia bibendum nulla sed consectetur. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean lacinia bibendum nulla sed consectetur. Nullam quis risus eget urna mollis ornare vel eu leo. </p>
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec ullamcorper nulla non metus auctor fringilla. Maecenas faucibus mollis interdum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. </p>
</div>
</div>
</div>
<div id="specs">
</div>
<div id="installing">
<div class="section">
<div class="grid7 firstcol">
<h1>Installing Webfonts</h1>
<p>Webfonts are supported by all major browser platforms but not all in the same way. There are currently four different font formats that must be included in order to target all browsers. This includes TTF, WOFF, EOT and SVG.</p>
<h2>1. Upload your webfonts</h2>
<p>You must upload your webfont kit to your website. They should be in or near the same directory as your CSS files.</p>
<h2>2. Include the webfont stylesheet</h2>
<p>A special CSS @font-face declaration helps the various browsers select the appropriate font it needs without causing you a bunch of headaches. Learn more about this syntax by reading the <a href="http://www.fontspring.com/blog/further-hardening-of-the-bulletproof-syntax">Fontspring blog post</a> about it. The code for it is as follows:</p>
<code>
@font-face{
font-family: 'MyWebFont';
src: url('WebFont.eot');
src: url('WebFont.eot?iefix') format('eot'),
url('WebFont.woff') format('woff'),
url('WebFont.ttf') format('truetype'),
url('WebFont.svg#webfont') format('svg');
}
</code>
<p>We've already gone ahead and generated the code for you. All you have to do is link to the stylesheet in your HTML, like this:</p>
<code>&lt;link rel=&quot;stylesheet&quot; href=&quot;stylesheet.css&quot; type=&quot;text/css&quot; charset=&quot;utf-8&quot; /&gt;</code>
<h2>3. Modify your own stylesheet</h2>
<p>To take advantage of your new fonts, you must tell your stylesheet to use them. Look at the original @font-face declaration above and find the property called "font-family." The name linked there will be what you use to reference the font. Prepend that webfont name to the font stack in the "font-family" property, inside the selector you want to change. For example:</p>
<code>p { font-family: 'MyWebFont', Arial, sans-serif; }</code>
<h2>4. Test</h2>
<p>Getting webfonts to work cross-browser <em>can</em> be tricky. Use the information in the sidebar to help you if you find that fonts aren't loading in a particular browser.</p>
</div>
<div class="grid5 sidebar">
<div class="box">
<h2>Troubleshooting<br />Font-Face Problems</h2>
<p>Having trouble getting your webfonts to load in your new website? Here are some tips to sort out what might be the problem.</p>
<h3>Fonts not showing in any browser</h3>
<p>This sounds like you need to work on the plumbing. You either did not upload the fonts to the correct directory, or you did not link the fonts properly in the CSS. If you've confirmed that all this is correct and you still have a problem, take a look at your .htaccess file and see if requests are getting intercepted.</p>
<h3>Fonts not loading in iPhone or iPad</h3>
<p>The most common problem here is that you are serving the fonts from an IIS server. IIS refuses to serve files that have unknown MIME types. If that is the case, you must set the MIME type for SVG to "image/svg+xml" in the server settings. Follow these instructions from Microsoft if you need help.</p>
<h3>Fonts not loading in Firefox</h3>
<p>The primary reason for this failure? You are still using a version Firefox older than 3.5. So upgrade already! If that isn't it, then you are very likely serving fonts from a different domain. Firefox requires that all font assets be served from the same domain. Lastly it is possible that you need to add WOFF to your list of MIME types (if you are serving via IIS.)</p>
<h3>Fonts not loading in IE</h3>
<p>Are you looking at Internet Explorer on an actual Windows machine or are you cheating by using a service like Adobe BrowserLab? Many of these screenshot services do not render @font-face for IE. Best to test it on a real machine.</p>
<h3>Fonts not loading in IE9</h3>
<p>IE9, like Firefox, requires that fonts be served from the same domain as the website. Make sure that is the case.</p>
</div>
</div>
</div>
</div>
</div>
<div id="footer">
<p>&copy;2010-2011 Fontspring. All rights reserved.</p>
</div>
</div>
</body>
</html>

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 969 KiB

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More