├── .gitignore ├── Makefile ├── README.md ├── Vagrantfile ├── kernel ├── boot.S ├── kernel.c ├── linker.ld ├── vga_driver.c └── vga_driver.h ├── requirements.txt ├── scripts └── build_cross_compiler.sh └── slides ├── Makefile └── source ├── _static ├── darwinmascot.png ├── openbsd.png ├── osllogo.png ├── popcorn.png ├── tux.png └── unix_history.png ├── conf.py └── index.rst /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore source code and binaries used for cross compilation 2 | compiler 3 | 4 | # Ignore generated files for running vagrant VMs 5 | .vagrant 6 | 7 | # Ignore vim swap files 8 | .*.sw[a-z] 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This is our cross compiler's target architecture 2 | TARGET=i686-elf 3 | # This is the path to our C Compiler 4 | CC=compiler/$(TARGET)/bin/$(TARGET)-gcc 5 | # This is the path to our assembler 6 | AS=compiler/$(TARGET)/bin/$(TARGET)-as 7 | # Here are the flags for our C Compiler 8 | CFLAGS= -std=c99 -ffreestanding -O2 -Wall -Wextra -fdiagnostics-color=auto 9 | # These are the flags for the linking step 10 | LDFLAGS= -lgcc -O2 -ffreestanding -nostdlib -fdiagnostics-color=auto 11 | 12 | all: bootloader kernel link 13 | 14 | # Boot the kernel! 15 | start: 16 | qemu-system-i386 -kernel build/popos.img 17 | 18 | # All of our object files, images, and executables will be stored here. 19 | build: 20 | mkdir build 21 | 22 | # This is the bootloader. We need to assemble it into an object file we can 23 | # link against. 24 | bootloader: build 25 | $(AS) kernel/boot.S -o build/boot.o 26 | 27 | # Build the kernel. 28 | kernel: build vga_driver 29 | $(CC) -c kernel/kernel.c -o build/kernel.o $(CFLAGS) 30 | 31 | # Link it all together into a single image 32 | link: build 33 | $(CC) -T kernel/linker.ld build/vga_driver.o build/boot.o build/kernel.o -o build/popos.img $(LDFLAGS) 34 | 35 | # This is how we write to the screen 36 | vga_driver: build 37 | $(CC) -c kernel/vga_driver.c -o build/vga_driver.o $(CFLAGS) 38 | 39 | # delete everything so we can start fresh 40 | clean: 41 | rm -rf build 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pop Open a Kernel 2 | ================= 3 | 4 | This is an introduction to writing a simple kernel for x86 5 | 6 | Talk Description 7 | ---------------- 8 | Ever wanted to build a simple kernel for a small computer? Curious how an OS 9 | starts and how it communicates with your keyboard and screen? Together, we'll 10 | build a simple kernel from scratch. No experience in assembly language or 11 | knowledge about CPU architecture is required, just some basic knowledge of 12 | C/C++ and curiosity about how things work under the hood. 13 | Description 14 | 15 | A kernel is such a fundamental, and much hyped, part of an operating system, 16 | but most people don’t know how one works. 17 | In this talk we’ll cover both the theory of kernel architecture design, and get 18 | our hands dirty tearing apart a simple one. 19 | We’ll start by describing the jobs of a kernel, then well talk about different 20 | architectures and ways of achieving these goals. 21 | Then we’ll look at the code from the bottom up, and describe how a kernel is 22 | compiled and linked. 23 | Next is the boot process. We’ll get in to the gritty details of what happens 24 | once the power button is pushed, describe the boot loader, how the kernel is 25 | loaded into memory, executed, and how the first user process starts. 26 | We’ll discuss hardware interrupts and protection rings. We’ll even have to 27 | learn a little bit of x86 assembly, but don’t be scared! 28 | Finally we’ll take a look at multitasking, paging, and maybe file systems, and 29 | system calls if we have time. 30 | This session will be hands on – we get to take a look at a toy kernel, boot it, 31 | watch it work and watch it fail. We’ll learn about hardware, low-level 32 | software, and algorithms for multitasking and paging. 33 | 34 | Building this kernel 35 | -------------------- 36 | First you'll probably need to build the x86 cross compiler, although your 37 | distro may provide one. Luckily, I made a 38 | script to do this for you, but it's pretty slow to fetch all of GCC's 39 | dependencies and compile them. 40 | ```sh 41 | $ sh scripts/build_cross_compiler.sh 42 | ``` 43 | This will build a GCC cross compiler for x86. 44 | 45 | Next, you'll need to be able to run this kernel. We'll need qemu, a program 46 | used for starting virtual machines. On an ubuntu like system, run 47 | ```sh 48 | $ sudo apt-get install qemu 49 | ``` 50 | 51 | Finally we can actually build and run the kernel! 52 | ```sh 53 | $ make 54 | $ make run 55 | ``` 56 | 57 | qemu will open in a new window and the kernel will print "Hello kernel!" This 58 | is a great way to get started. 59 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |c| 2 | c.vm.box = "centos7" 3 | c.vm.box_url = "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-7.0_chef-provisionerless.box" 4 | c.vm.hostname = "pop" 5 | # c.vm.network(:forwarded_port, {:guest=>8000, :host=>8080}) 6 | c.vm.synced_folder ".", "/home/vagrant/project" 7 | c.vm.provider :virtualbox do |p| 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /kernel/boot.S: -------------------------------------------------------------------------------- 1 | # Declare constants used for creating a multiboot header. 2 | .set ALIGN, 1<<0 # align loaded modules on page boundaries 3 | .set MEMINFO, 1<<1 # provide memory map 4 | .set FLAGS, ALIGN | MEMINFO # this is the Multiboot 'flag' field 5 | .set MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header 6 | .set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot 7 | 8 | # Declare a header as in the Multiboot Standard. We put this into a special 9 | # section so we can force the header to be in the start of the final program. 10 | # You don't need to understand all these details as it is just magic values that 11 | # is documented in the multiboot standard. The bootloader will search for this 12 | # magic sequence and recognize us as a multiboot kernel. 13 | .section .multiboot 14 | .align 4 15 | .long MAGIC 16 | .long FLAGS 17 | .long CHECKSUM 18 | 19 | # Currently the stack pointer register (esp) points at anything and using it may 20 | # cause massive harm. Instead, we'll provide our own stack. We will allocate 21 | # room for a small temporary stack by creating a symbol at the bottom of it, 22 | # then allocating 16384 bytes for it, and finally creating a symbol at the top. 23 | .section .bootstrap_stack 24 | stack_bottom: 25 | .skip 16384 # 16 KiB 26 | stack_top: 27 | 28 | # The linker script specifies _start as the entry point to the kernel and the 29 | # bootloader will jump to this position once the kernel has been loaded. It 30 | # doesn't make sense to return from this function as the bootloader is gone. 31 | .section .text 32 | .global _start 33 | .type _start, @function 34 | _start: 35 | # Welcome to kernel mode! We now have sufficient code for the bootloader to 36 | # load and run our operating system. It doesn't do anything interesting yet. 37 | # Perhaps we would like to call printf("Hello, World\n"). You should now 38 | # realize one of the profound truths about kernel mode: There is nothing 39 | # there unless you provide it yourself. There is no printf function. There 40 | # is no header. If you want a function, you will have to code it 41 | # yourself. And that is one of the best things about kernel development: 42 | # you get to make the entire system yourself. You have absolute and complete 43 | # power over the machine, there are no security restrictions, no safe 44 | # guards, no debugging mechanisms, there is nothing but what you build. 45 | 46 | # By now, you are perhaps tired of assembly language. You realize some 47 | # things simply cannot be done in C, such as making the multiboot header in 48 | # the right section and setting up the stack. However, you would like to 49 | # write the operating system in a higher level language, such as C or C++. 50 | # To that end, the next task is preparing the processor for execution of 51 | # such code. C doesn't expect much at this point and we only need to set up 52 | # a stack. Note that the processor is not fully initialized yet and stuff 53 | # such as floating point instructions are not available yet. 54 | 55 | # To set up a stack, we simply set the esp register to point to the top of 56 | # our stack (as it grows downwards). 57 | movl $stack_top, %esp 58 | 59 | # We are now ready to actually execute C code. We cannot embed that in an 60 | # assembly file, so we'll create a kernel.c file in a moment. In that file, 61 | # we'll create a C entry point called kernel_main and call it here. 62 | call kernel_main 63 | 64 | # In case the function returns, we'll want to put the computer into an 65 | # infinite loop. To do that, we use the clear interrupt ('cli') instruction 66 | # to disable interrupts, the halt instruction ('hlt') to stop the CPU until 67 | # the next interrupt arrives, and jumping to the halt instruction if it ever 68 | # continues execution, just to be safe. We will create a local label rather 69 | # than real symbol and jump to there endlessly. 70 | cli 71 | hlt 72 | .Lhang: 73 | jmp .Lhang 74 | 75 | # Set the size of the _start symbol to the current location '.' minus its start. 76 | # This is useful when debugging or when you implement call tracing. 77 | .size _start, . - _start 78 | 79 | -------------------------------------------------------------------------------- /kernel/kernel.c: -------------------------------------------------------------------------------- 1 | #include "vga_driver.h" 2 | 3 | void kernel_main() { 4 | vga_initialize(); 5 | vga_putstr("Hello kernel!"); 6 | } 7 | -------------------------------------------------------------------------------- /kernel/linker.ld: -------------------------------------------------------------------------------- 1 | /* The bootloader will look at this image and start execution at the symbol 2 | designated as the entry point. */ 3 | ENTRY(_start) 4 | 5 | /* Tell where the various sections of the object files will be put in the final 6 | kernel image. */ 7 | SECTIONS 8 | { 9 | /* Begin putting sections at 1 MiB, a conventional place for kernels to be 10 | loaded at by the bootloader. */ 11 | . = 1M; 12 | 13 | /* First put the multiboot header, as it is required to be put very early 14 | early in the image or the bootloader won't recognize the file format. 15 | Next we'll put the .text section. */ 16 | .text BLOCK(4K) : ALIGN(4K) 17 | { 18 | *(.multiboot) 19 | *(.text) 20 | } 21 | 22 | /* Read-only data. */ 23 | .rodata BLOCK(4K) : ALIGN(4K) 24 | { 25 | *(.rodata) 26 | } 27 | 28 | /* Read-write data (initialized) */ 29 | .data BLOCK(4K) : ALIGN(4K) 30 | { 31 | *(.data) 32 | } 33 | 34 | /* Read-write data (uninitialized) and stack */ 35 | .bss BLOCK(4K) : ALIGN(4K) 36 | { 37 | *(COMMON) 38 | *(.bss) 39 | *(.bootstrap_stack) 40 | } 41 | 42 | /* The compiler may produce other sections, by default it will put them in 43 | a segment with the same name. Simply add stuff here as needed. */ 44 | } 45 | -------------------------------------------------------------------------------- /kernel/vga_driver.c: -------------------------------------------------------------------------------- 1 | #include "vga_driver.h" 2 | 3 | uint8_t make_color(enum vga_color fg, enum vga_color bg) { 4 | return fg | bg << 4; 5 | } 6 | 7 | uint16_t make_vga_entry(char c, uint8_t color) { 8 | uint16_t c16 = c; 9 | uint16_t color16 = color; 10 | return c16 | color16 << 8; 11 | } 12 | 13 | void vga_initialize() { 14 | cursor_col = 0; 15 | cursor_row = 0; 16 | BG_COLOR = make_color(COLOR_LIGHT_GREY, COLOR_BLACK); 17 | vga_buffer = (uint16_t*) 0xB8000; 18 | 19 | uint16_t vga_entry = make_vga_entry(' ', BG_COLOR); 20 | for (size_t i = 0; i < VGA_WIDTH * VGA_HEIGHT; i++) { 21 | vga_buffer[i] = vga_entry; 22 | } 23 | vga_buffer[0] = make_vga_entry('H', make_color(COLOR_GREEN, COLOR_BLACK)); 24 | vga_buffer[1] = make_vga_entry('i', make_color(COLOR_GREEN, COLOR_BLACK)); 25 | } 26 | 27 | int vga_putchar(int c) { 28 | uint8_t text_color = make_color(COLOR_GREEN, COLOR_BLACK); 29 | vga_buffer[cursor_row * VGA_WIDTH + cursor_col] = make_vga_entry(c, text_color); 30 | cursor_col++; 31 | if (cursor_col > VGA_WIDTH) { 32 | cursor_col = 0; 33 | cursor_row++; 34 | // FIXME: write a function to push lines up the screen 35 | if (cursor_row > VGA_HEIGHT) { 36 | vga_initialize(); 37 | cursor_row = 0; 38 | } 39 | } 40 | return c; 41 | } 42 | 43 | int vga_putstr(char* data) { 44 | int i; 45 | for (i = 0; data[i] != '\0'; ++i) { 46 | vga_putchar(data[i]); 47 | } 48 | return i; 49 | } 50 | 51 | void vga_copy_up_lines() { 52 | } 53 | -------------------------------------------------------------------------------- /kernel/vga_driver.h: -------------------------------------------------------------------------------- 1 | #ifndef VGADRIVER_H 2 | #define VGADRIVER_H 3 | 4 | #include 5 | #include 6 | 7 | enum vga_color { 8 | COLOR_BLACK = 0, 9 | COLOR_BLUE = 1, 10 | COLOR_GREEN = 2, 11 | COLOR_CYAN = 3, 12 | COLOR_RED = 4, 13 | COLOR_MAGENTA = 5, 14 | COLOR_BROWN = 6, 15 | COLOR_LIGHT_GREY = 7, 16 | COLOR_DARK_GREY = 8, 17 | COLOR_LIGHT_BLUE = 9, 18 | COLOR_LIGHT_GREEN = 10, 19 | COLOR_LIGHT_CYAN = 11, 20 | COLOR_LIGHT_RED = 12, 21 | COLOR_LIGHT_MAGENTA = 13, 22 | COLOR_LIGHT_BROWN = 14, 23 | COLOR_WHITE = 15, 24 | }; 25 | uint16_t BG_COLOR; 26 | 27 | static const size_t VGA_HEIGHT = 24; 28 | static const size_t VGA_WIDTH = 80; 29 | 30 | 31 | size_t cursor_col; 32 | size_t cursor_row; 33 | uint16_t* vga_buffer; 34 | 35 | int vga_putchar(int c); 36 | int vga_putstr(char* data); 37 | void vga_initialize(); 38 | uint16_t make_vga_entry(char c, uint8_t color); 39 | uint8_t make_color(enum vga_color fg, enum vga_color bg); 40 | 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Jinja2==2.7.3 2 | MarkupSafe==0.23 3 | Pygments==2.0.1 4 | Sphinx==1.2.3 5 | docutils==0.12 6 | hieroglyph==0.6.5 7 | six==1.8.0 8 | wsgiref==0.1.2 9 | -------------------------------------------------------------------------------- /scripts/build_cross_compiler.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TARGET=i686-elf 4 | PREFIX=$(pwd)/compiler/$TARGET 5 | PATH=$PATH:$PREFIX/bin 6 | CFLAGS=" -g -O2" 7 | # if we're on a mac, try our damndest to use real GCC not clang 8 | if [ "$(uname)" == "Darwin" ]; then 9 | export CC=gcc 10 | export CXX=g++ 11 | alias CC=gcc 12 | alias CXX=g++ 13 | fi 14 | echo "Nuking ./compiler/build and ./compiler/$TARGET and recreating directories" 15 | rm -rf compiler/build 16 | rm -rf compiler/$TARGET 17 | mkdir -p compiler/build 18 | mkdir -p compiler/$TARGET 19 | 20 | if [[ ! -d compiler/src ]]; then 21 | mkdir -p compiler/src 22 | cd compiler/src 23 | 24 | echo 'Downloading sources' 25 | echo $(pwd) 26 | wget http://ftp.gnu.org/gnu/binutils/binutils-2.24.tar.gz 27 | wget ftp://ftp.gnu.org/gnu/gcc/gcc-4.9.1/gcc-4.9.1.tar.gz 28 | wget https://gmplib.org/download/gmp/gmp-6.0.0a.tar.xz 29 | wget http://www.mpfr.org/mpfr-current/mpfr-3.1.2.tar.xz 30 | wget ftp://ftp.gnu.org/gnu/mpc/mpc-1.0.2.tar.gz 31 | if [ "$(uname)" == "Darwin" ]; then 32 | wget http://www.bastoul.net/cloog/pages/download/count.php3?url=./cloog-parma-0.16.1.tar.gz 33 | wget http://isl.gforge.inria.fr/isl-0.13.tar.xz 34 | fi 35 | 36 | echo 'Extracting archives' 37 | echo $(pwd) 38 | tar xzf binutils-2.24.tar.gz 39 | tar xzf gcc-4.9.1.tar.gz 40 | tar xzf mpc-1.0.2.tar.gz 41 | tar xJf gmp-6.0.0a.tar.xz 42 | tar xJf mpfr-3.1.2.tar.xz 43 | mv gmp-6.0.0a gcc-4.9.1/gmp 44 | mv mpfr-3.1.2 gcc-4.9.1/mpfr 45 | mv mpc-1.0.2 gcc-4.9.1/mpc 46 | if [ "$(uname)" == "Darwin" ]; then 47 | tar xzf cloog-parma-0.16.1.tar.gz 48 | tar xJf isl-0.13.tar.xz 49 | mv cloog-parma-0.16.1 gcc-4.9.1/cloog 50 | mv isl-0.13 gcc-4.9.1/isl 51 | fi 52 | cd - 53 | fi 54 | 55 | echo 'Building binutils' 56 | echo $(pwd) 57 | mkdir compiler/build/binutils 58 | cd compiler/build/binutils 59 | ../../src/binutils-2.24/configure --target=$TARGET --prefix="$PREFIX" --with-sysroot --disable-nls --disable-werror 60 | make 61 | make install 62 | cd - 63 | 64 | echo 'Building gcc' 65 | echo $(pwd) 66 | mkdir compiler/build/gcc 67 | cd compiler/build/gcc 68 | ../../src/gcc-4.9.1/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c,c++ --without-headers 69 | make all-gcc 70 | make all-target-libgcc 71 | make install-gcc 72 | make install-target-libgcc 73 | cd - 74 | 75 | echo 'Done!' 76 | -------------------------------------------------------------------------------- /slides/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PopOpenaKernel.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PopOpenaKernel.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/PopOpenaKernel" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PopOpenaKernel" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | 179 | 180 | slides: 181 | $(SPHINXBUILD) -b slides $(ALLSPHINXOPTS) $(BUILDDIR)/slides 182 | @echo "Build finished. The HTML slides are in $(BUILDDIR)/slides." 183 | 184 | -------------------------------------------------------------------------------- /slides/source/_static/darwinmascot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iankronquist/pop_goes_the_kernel/2c54b1039368e9e83bfeb9e80bb2014a7cf578de/slides/source/_static/darwinmascot.png -------------------------------------------------------------------------------- /slides/source/_static/openbsd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iankronquist/pop_goes_the_kernel/2c54b1039368e9e83bfeb9e80bb2014a7cf578de/slides/source/_static/openbsd.png -------------------------------------------------------------------------------- /slides/source/_static/osllogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iankronquist/pop_goes_the_kernel/2c54b1039368e9e83bfeb9e80bb2014a7cf578de/slides/source/_static/osllogo.png -------------------------------------------------------------------------------- /slides/source/_static/popcorn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iankronquist/pop_goes_the_kernel/2c54b1039368e9e83bfeb9e80bb2014a7cf578de/slides/source/_static/popcorn.png -------------------------------------------------------------------------------- /slides/source/_static/tux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iankronquist/pop_goes_the_kernel/2c54b1039368e9e83bfeb9e80bb2014a7cf578de/slides/source/_static/tux.png -------------------------------------------------------------------------------- /slides/source/_static/unix_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iankronquist/pop_goes_the_kernel/2c54b1039368e9e83bfeb9e80bb2014a7cf578de/slides/source/_static/unix_history.png -------------------------------------------------------------------------------- /slides/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Pop Open a Kernel documentation build configuration file, created by 4 | # sphinx-quickstart on Thu Nov 20 18:02:33 2014. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.doctest', 33 | 'sphinx.ext.intersphinx', 34 | 'sphinx.ext.todo', 35 | 'sphinx.ext.coverage', 36 | 'sphinx.ext.pngmath', 37 | 'sphinx.ext.ifconfig', 38 | ] 39 | 40 | # Add any paths that contain templates here, relative to this directory. 41 | templates_path = ['_templates'] 42 | 43 | # The suffix of source filenames. 44 | source_suffix = '.rst' 45 | 46 | # The encoding of source files. 47 | #source_encoding = 'utf-8-sig' 48 | 49 | # The master toctree document. 50 | master_doc = 'index' 51 | 52 | # General information about the project. 53 | project = u'Pop Open a Kernel' 54 | copyright = u'2014, Ian Kronquist' 55 | 56 | # The version info for the project you're documenting, acts as replacement for 57 | # |version| and |release|, also used in various other places throughout the 58 | # built documents. 59 | # 60 | # The short X.Y version. 61 | version = '2014.11.20' 62 | # The full version, including alpha/beta/rc tags. 63 | release = '2014.11.20' 64 | 65 | # The language for content autogenerated by Sphinx. Refer to documentation 66 | # for a list of supported languages. 67 | #language = None 68 | 69 | # There are two options for replacing |today|: either, you set today to some 70 | # non-false value, then it is used: 71 | #today = '' 72 | # Else, today_fmt is used as the format for a strftime call. 73 | #today_fmt = '%B %d, %Y' 74 | 75 | # List of patterns, relative to source directory, that match files and 76 | # directories to ignore when looking for source files. 77 | exclude_patterns = [] 78 | 79 | # The reST default role (used for this markup: `text`) to use for all 80 | # documents. 81 | #default_role = None 82 | 83 | # If true, '()' will be appended to :func: etc. cross-reference text. 84 | #add_function_parentheses = True 85 | 86 | # If true, the current module name will be prepended to all description 87 | # unit titles (such as .. function::). 88 | #add_module_names = True 89 | 90 | # If true, sectionauthor and moduleauthor directives will be shown in the 91 | # output. They are ignored by default. 92 | #show_authors = False 93 | 94 | # The name of the Pygments (syntax highlighting) style to use. 95 | pygments_style = 'sphinx' 96 | 97 | # A list of ignored prefixes for module index sorting. 98 | #modindex_common_prefix = [] 99 | 100 | # If true, keep warnings as "system message" paragraphs in the built documents. 101 | #keep_warnings = False 102 | 103 | 104 | # -- Options for HTML output ---------------------------------------------- 105 | 106 | # The theme to use for HTML and HTML Help pages. See the documentation for 107 | # a list of builtin themes. 108 | html_theme = 'default' 109 | 110 | # Theme options are theme-specific and customize the look and feel of a theme 111 | # further. For a list of options available for each theme, see the 112 | # documentation. 113 | #html_theme_options = {} 114 | 115 | # Add any paths that contain custom themes here, relative to this directory. 116 | #html_theme_path = [] 117 | 118 | # The name for this set of Sphinx documents. If None, it defaults to 119 | # " v documentation". 120 | #html_title = None 121 | 122 | # A shorter title for the navigation bar. Default is the same as html_title. 123 | #html_short_title = None 124 | 125 | # The name of an image file (relative to this directory) to place at the top 126 | # of the sidebar. 127 | #html_logo = None 128 | 129 | # The name of an image file (within the static path) to use as favicon of the 130 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 131 | # pixels large. 132 | #html_favicon = None 133 | 134 | # Add any paths that contain custom static files (such as style sheets) here, 135 | # relative to this directory. They are copied after the builtin static files, 136 | # so a file named "default.css" will overwrite the builtin "default.css". 137 | html_static_path = ['_static'] 138 | 139 | # Add any extra paths that contain custom files (such as robots.txt or 140 | # .htaccess) here, relative to this directory. These files are copied 141 | # directly to the root of the documentation. 142 | #html_extra_path = [] 143 | 144 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 145 | # using the given strftime format. 146 | #html_last_updated_fmt = '%b %d, %Y' 147 | 148 | # If true, SmartyPants will be used to convert quotes and dashes to 149 | # typographically correct entities. 150 | #html_use_smartypants = True 151 | 152 | # Custom sidebar templates, maps document names to template names. 153 | #html_sidebars = {} 154 | 155 | # Additional templates that should be rendered to pages, maps page names to 156 | # template names. 157 | #html_additional_pages = {} 158 | 159 | # If false, no module index is generated. 160 | #html_domain_indices = True 161 | 162 | # If false, no index is generated. 163 | #html_use_index = True 164 | 165 | # If true, the index is split into individual pages for each letter. 166 | #html_split_index = False 167 | 168 | # If true, links to the reST sources are added to the pages. 169 | #html_show_sourcelink = True 170 | 171 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 172 | #html_show_sphinx = True 173 | 174 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 175 | #html_show_copyright = True 176 | 177 | # If true, an OpenSearch description file will be output, and all pages will 178 | # contain a tag referring to it. The value of this option must be the 179 | # base URL from which the finished HTML is served. 180 | #html_use_opensearch = '' 181 | 182 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 183 | #html_file_suffix = None 184 | 185 | # Output file base name for HTML help builder. 186 | htmlhelp_basename = 'PopOpenaKerneldoc' 187 | 188 | 189 | # -- Options for LaTeX output --------------------------------------------- 190 | 191 | latex_elements = { 192 | # The paper size ('letterpaper' or 'a4paper'). 193 | #'papersize': 'letterpaper', 194 | 195 | # The font size ('10pt', '11pt' or '12pt'). 196 | #'pointsize': '10pt', 197 | 198 | # Additional stuff for the LaTeX preamble. 199 | #'preamble': '', 200 | } 201 | 202 | # Grouping the document tree into LaTeX files. List of tuples 203 | # (source start file, target name, title, 204 | # author, documentclass [howto, manual, or own class]). 205 | latex_documents = [ 206 | ('index', 'PopOpenaKernel.tex', u'Pop Open a Kernel Documentation', 207 | u'Ian Kronquist', 'manual'), 208 | ] 209 | 210 | # The name of an image file (relative to this directory) to place at the top of 211 | # the title page. 212 | #latex_logo = None 213 | 214 | # For "manual" documents, if this is true, then toplevel headings are parts, 215 | # not chapters. 216 | #latex_use_parts = False 217 | 218 | # If true, show page references after internal links. 219 | #latex_show_pagerefs = False 220 | 221 | # If true, show URL addresses after external links. 222 | #latex_show_urls = False 223 | 224 | # Documents to append as an appendix to all manuals. 225 | #latex_appendices = [] 226 | 227 | # If false, no module index is generated. 228 | #latex_domain_indices = True 229 | 230 | 231 | # -- Options for manual page output --------------------------------------- 232 | 233 | # One entry per manual page. List of tuples 234 | # (source start file, name, description, authors, manual section). 235 | man_pages = [ 236 | ('index', 'popopenakernel', u'Pop Open a Kernel Documentation', 237 | [u'Ian Kronquist'], 1) 238 | ] 239 | 240 | # If true, show URL addresses after external links. 241 | #man_show_urls = False 242 | 243 | 244 | # -- Options for Texinfo output ------------------------------------------- 245 | 246 | # Grouping the document tree into Texinfo files. List of tuples 247 | # (source start file, target name, title, author, 248 | # dir menu entry, description, category) 249 | texinfo_documents = [ 250 | ('index', 'PopOpenaKernel', u'Pop Open a Kernel Documentation', 251 | u'Ian Kronquist', 'PopOpenaKernel', 'One line description of project.', 252 | 'Miscellaneous'), 253 | ] 254 | 255 | # Documents to append as an appendix to all manuals. 256 | #texinfo_appendices = [] 257 | 258 | # If false, no module index is generated. 259 | #texinfo_domain_indices = True 260 | 261 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 262 | #texinfo_show_urls = 'footnote' 263 | 264 | # If true, do not generate a @detailmenu in the "Top" node's menu. 265 | #texinfo_no_detailmenu = False 266 | 267 | 268 | # -- Hieroglyph Slide Configuration ------------ 269 | 270 | extensions += [ 271 | 'hieroglyph', 272 | ] 273 | 274 | slide_theme = 'single-level' 275 | slide_levels = 3 276 | 277 | # Place custom static assets in the _static directory and uncomment 278 | # the following lines to include them 279 | 280 | # slide_theme_options = { 281 | # 'custom_css': 'custom.css', 282 | # 'custom_js': 'custom.js', 283 | # } 284 | 285 | # ---------------------------------------------- 286 | 287 | 288 | 289 | # Example configuration for intersphinx: refer to the Python standard library. 290 | intersphinx_mapping = {'http://docs.python.org/': None} 291 | -------------------------------------------------------------------------------- /slides/source/index.rst: -------------------------------------------------------------------------------- 1 | Pop Open a Kernel 2 | ================= 3 | 4 | .. figure:: /_static/popcorn.png 5 | :width: 50% 6 | :align: center 7 | 8 | Who am I? 9 | --------- 10 | 11 | Ian Kronquist. 12 | 13 | Developer at the Oregon State University Open Source Lab and a student. 14 | 15 | iankronquist@gmail.com 16 | 17 | muricula on Freenode, PSU IRC, and Mozilla IRC networks. 18 | 19 | @muricula on twitter. 20 | 21 | 22 | .. figure:: /_static/osllogo.png 23 | :width: 50% 24 | :align: center 25 | 26 | Changes in the program (sorry!) 27 | ------------------------------- 28 | 29 | * The example kernel is x86, not ARM. There are more examples on the interwebs 30 | for x86 kernels. 31 | * This session won't be as hands on as I hoped. It turns out getting everyone 32 | on the same page is hard. 33 | * I may run short. That's okay, it means more time for questions! 34 | 35 | What is a kernel and What Does it Do? 36 | ------------------------------------- 37 | * The kernel is at the heart of the OS. It is among the first programs to start 38 | up and the only one always running. 39 | * The kernel is the middleman between "normal" or userspace programs and your 40 | computers hardware. 41 | 42 | .. figure:: /_static/tux.png 43 | :width: 30% 44 | :align: center 45 | 46 | 47 | 48 | What's the difference between a kernel and an Operating System? 49 | --------------------------------------------------------------- 50 | 51 | * A kernel is just one part of an operating system, and it's hardly the only 52 | part which matters. 53 | * An operating system might include init systems, to start up programs and 54 | windowing managers, to draw windows on the screen, 55 | * Most modern OSs include everything and the kitchen sink, including web 56 | browsers, office suites, desktop environments, you name it. 57 | * Linux is the name of a *kernel*. Pedants like to call the whole OS 58 | *GNU/Linux*. 59 | 60 | A Brief History of Kernels 61 | -------------------------- 62 | 63 | * In the old old days only one program would run at a time. The level of safety 64 | * and abstraction a kernel provides wasn't needed. 65 | * Most research into kernels happened in the 1970s and 1980s. 66 | * Most kernels widely used today have been around at least since the 1990s. 67 | * Unix was the name of an operating system developed by Ken Thompson and Dennis 68 | Ritchie. They designed the C programming language in order to write Unix. 69 | 70 | Kernels in Use Today 71 | -------------------- 72 | 73 | * XNU Kernel (the modern OSX kernel) 1996 74 | * Windows NT (the modern Windows kernel) 1993 75 | * Solaris 1992 76 | * Linux 1991 77 | * GNU Hurd (A FOSS Linux competitor) 1983 78 | * BSD 1977 79 | * Unix 1973 80 | 81 | A History of Unix Kernels 82 | ------------------------- 83 | 84 | .. figure:: /_static/unix_history.png 85 | :width: 100% 86 | :align: center 87 | 88 | 89 | Why is studying kernels relevant in the 21st century? 90 | ----------------------------------------------------- 91 | Really interesting stuff is happening! In Linux we're seeing 92 | 93 | 94 | * Reboot free updates with KSplice. 95 | * Linux Containers, a technology originally developed in 2008, is now 96 | almost mature enough to use for production tools like Docker. 97 | * Formal verification of Linux systems is an open challenge put out by some 98 | Linux kernel developers. 99 | 100 | 101 | Different Kernel Architectures 102 | ------------------------------ 103 | 104 | There are three types of Kernel architectures. 105 | 106 | * Microkernel 107 | - Runs drivers in userspace. 108 | - Task switching between userspace and kernel mode is expensive. 109 | - Examples: Various embedded kernels you've never heard of. 110 | * Monolithic kernel 111 | - Runs drivers in kernel mode. 112 | - Broken drivers can take down the whole system. 113 | - Examples: Linux, BSD 114 | 115 | .. nextslide:: 116 | 117 | * Hybrid kernel 118 | - A bit of both. Core services are baked into the kernel, but it offers 119 | ways to run other drivers in userspace. 120 | - Examples: Windows, OS X 121 | * A unikernel is a kernel which runs one program. 122 | 123 | .. figure:: /_static/darwinmascot.png 124 | :width: 30% 125 | :align: center 126 | 127 | - Either really old fashioned or really cutting edge. 128 | 129 | Kernel Modules 130 | -------------- 131 | 132 | It's really handy to be able to load drivers while the kernel is running 133 | without rebooting. Linux does this using kernel modules. 134 | 135 | A compromise between monolithic kernels and hybrid kernels. 136 | 137 | Run ``lsmod`` to see what kernel modules your machine is running. 138 | 139 | .. figure:: /_static/openbsd.png 140 | :width: 50% 141 | :align: center 142 | 143 | 144 | User mode and Kernel mode 145 | ------------------------- 146 | 147 | * Most kernels use hardware memory protection. 148 | * At boot, the kernel set up the processor so that some parts of memory can 149 | only hold kernel code, and other parts can only hold user code. Likewise, 150 | some parts of memory can only hold data. 151 | * There are four different protection "rings". The kernel runs on ring 0. 152 | User code runs on ring 3. Rings 1 & 2 aren't really used anymore. 153 | * If code running in user mode (like any program you write) tries to access 154 | memory it doesn't have permission to use, it will trigger page fault, which 155 | is a special type of interrupt. The kernel typically just kills the program. 156 | 157 | Hardware Interrupts 158 | ------------------- 159 | * How does the kernel listen to the hardware clock, or process keystrokes? 160 | * Every time you press a key, a signal is sent to the process or which triggers 161 | a hardware interrupt. 162 | * The kernel stops whatever it's doing, looks up the interrupt number in a 163 | table, and executes a function in the table which might read the message the 164 | keyboard saying which key was pressed. 165 | 166 | Making Syscalls 167 | --------------- 168 | * An interface between your program and the kernel. Typically a function you 169 | call in your program. 170 | * Any time you read from a file with ``read`` or ``fread`` you make a syscall. 171 | * Your program talks to the kernel, which talks to the file system driver, 172 | Which talks to your hard-drive which reads some data and passes it back up 173 | the chain. 174 | * How syscalls are actually made is architecture dependent. 175 | 176 | Linux Syscalls on x86 177 | --------------------- 178 | * Syscalls are kept in a table. The table describes what number the syscall is, 179 | what calling convention to use (that's an assembly thing) and a pointer to 180 | the function in the kernel which implements the syscall. 181 | * Your program loads a number representing which syscall to make into the eax 182 | register, and triggers interrupt 0x80. 183 | * The kernel's interrupt handler looks up its function in the table which 184 | implements the syscall, and calls it. 185 | 186 | 187 | Questions? 188 | ---------- 189 | Ian Kronquist. 190 | 191 | iankronquist@gmail.com 192 | 193 | muricula on Freenode, PSU IRC, and Mozilla IRC networks. 194 | 195 | @muricula on twitter. 196 | 197 | 198 | 199 | My Kernel 200 | --------- 201 | 202 | * It's written in C and some x86 assembly. 203 | * It boots and prints "Hello Kernel" to the screen. 204 | 205 | 206 | What Do I Need to Know to Write a Simple One? 207 | --------------------------------------------- 208 | 209 | * Surprisingly little! 210 | * Some basic C/C++ 211 | * However, be prepared to learn a *lot* 212 | 213 | 214 | What Do I Need to Get Started? 215 | ------------------------------ 216 | 217 | We will start by running the kernel in a virtual machine. 218 | 219 | * I'm going to install you're running Ubuntu or something similar. 220 | * I'm going to assume you know some C or C++. Maybe you can write a program 221 | which fills a linked list with the first 100 Fibonacci numbers. 222 | * Install qemu, which is for running virtual machines. 223 | `sudo apt-get install qemu` 224 | * You will need to build a cross-compiler. 225 | This may actually be the hardest part. 226 | 227 | What is a Cross-Compiler? 228 | ------------------------- 229 | 230 | * The Kernel runs on a different hardware architecture than your laptop. 231 | * Your computer is probably x86_64. 232 | * We need to build a special version of GCC to compile code. 233 | * Luckily I wrote a script to make this easier for you! 234 | No guarantees that it will work - building complicated programs like GCC can 235 | get complicated. 236 | 237 | 238 | Questions? 239 | ---------- 240 | Ian Kronquist. 241 | 242 | iankronquist@gmail.com 243 | 244 | muricula on Freenode, PSU IRC, and Mozilla IRC networks. 245 | 246 | @muricula on twitter. 247 | 248 | 249 | --------------------------------------------------------------------------------