Compare commits

..

21 Commits

Author SHA1 Message Date
Philipp Oppermann
ca42c45c5e Switch to new multiboot2 crate 2015-11-12 17:06:07 +01:00
Philipp Oppermann
a4835b6778 Init allocator to make allocations work 2015-10-10 00:51:56 +02:00
Philipp Oppermann
d790fc20e5 Add an allocator 2015-10-09 17:52:00 +02:00
Philipp Oppermann
97a0da1bb9 Add spinlock dependency 2015-10-09 17:50:43 +02:00
Philipp Oppermann
3b762cafe0 Add unmap assertion 2015-10-09 15:16:36 +02:00
Philipp Oppermann
51949e80eb Remove old paging code 2015-10-09 15:14:22 +02:00
Philipp Oppermann
430181b9d7 Fix frame stack filling 2015-10-09 15:13:51 +02:00
Philipp Oppermann
bb593c2f63 WIP 2015-10-08 19:18:23 +02:00
Philipp Oppermann
f59b6c03d6 Print panic message 2015-10-08 18:31:16 +02:00
Philipp Oppermann
abd6e48c08 Add a dynamic growing frame stack 2015-10-08 01:54:46 +02:00
Philipp Oppermann
10ddcead2d wip 2015-09-15 14:57:09 +02:00
Philipp Oppermann
d27d36fdd0 WIP: Add paging and section remapping code 2015-09-15 14:57:09 +02:00
Philipp Oppermann
8090c2a752 Page align all sections as they will be individually mapped 2015-09-15 14:57:09 +02:00
Philipp Oppermann
95d6d30c29 Merge multiboot section to .text section
All sections must be page aligned to set the right privileges, so we would many padding bytes otherwise.
2015-09-15 14:57:08 +02:00
Philipp Oppermann
23e8270a60 Enable the no-execute feature in page tables 2015-09-15 14:57:08 +02:00
Philipp Oppermann
2655ac093c wip 2015-09-15 14:57:08 +02:00
Philipp Oppermann
812396d473 Add bitflags macro 2015-09-15 14:57:08 +02:00
Philipp Oppermann
7391a7a9eb [unfinished] Add Multiboot 2 crate and load Multiboot structure
Conflicts:
	Cargo.toml
	src/lib.rs

Conflicts:
	Cargo.toml
2015-09-15 14:57:08 +02:00
Philipp Oppermann
6713e55073 Pass Multiboot structure pointer as argument 2015-09-15 14:57:08 +02:00
Philipp Oppermann
88455c3f85 use vga buffer module for test output 2015-09-15 14:56:31 +02:00
Philipp Oppermann
a06577c685 add VGA text buffer module 2015-09-15 14:56:31 +02:00
381 changed files with 1248 additions and 79465 deletions

13
.github/FUNDING.yml vendored
View File

@@ -1,13 +0,0 @@
# These are supported funding model platforms
github: [phil-opp] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
custom: ['https://donorbox.org/phil-opp'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
patreon: phil_opp # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: phil-opp # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username

View File

@@ -1,107 +0,0 @@
name: Blog
on:
push:
branches:
- '*'
- '!staging.tmp'
tags:
- '*'
pull_request:
schedule:
- cron: '0 0 1/4 * *' # every 4 days
jobs:
build_site:
name: "Zola Build"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: 'Download Zola'
run: curl -sL https://github.com/getzola/zola/releases/download/v0.19.0/zola-v0.19.0-x86_64-unknown-linux-gnu.tar.gz | tar zxv
- name: 'Install Python Libraries'
run: python -m pip install --user -r requirements.txt
working-directory: "blog"
- name: "Run before_build.py script"
run: python before_build.py
working-directory: "blog"
- name: "Build Site"
run: ../zola build
working-directory: "blog"
- name: Upload Generated Site
uses: actions/upload-artifact@v4
with:
name: generated_site
path: blog/public
check_spelling:
name: "Check Spelling"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Typo Check
uses: crate-ci/typos@v1.1.9
with:
files: blog
deploy_site:
name: "Deploy Generated Site"
runs-on: ubuntu-latest
needs: [build_site, check_spelling]
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'schedule')
steps:
- name: "Download Generated Site"
uses: actions/download-artifact@v4
with:
name: generated_site
path: generated_site
- name: Setup SSH Keys and known_hosts
run: |
mkdir -p ~/.ssh
ssh-keyscan github.com >> ~/.ssh/known_hosts
ssh-agent -a $SSH_AUTH_SOCK > /dev/null
ssh-add - <<< "$deploy_key"
echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK" >> $GITHUB_ENV
env:
SSH_AUTH_SOCK: /tmp/ssh_agent.sock
deploy_key: ${{ secrets.DEPLOY_SSH_KEY }}
- name: "Clone blog_os_deploy Repo"
run: git clone git@github.com:phil-opp/blog_os_deploy.git --branch gh-pages
- name: "Set Up Git Identity"
run: |
git config --local user.name "GitHub Actions Deploy"
git config --local user.email "github-actions-deploy@phil-opp.com"
working-directory: "blog_os_deploy"
- name: "Delete Old Content"
run: "rm -r ./*"
working-directory: "blog_os_deploy"
- name: "Add New Content"
run: cp -r generated_site/* blog_os_deploy
- name: "Commit New Content"
run: |
git add .
git commit --allow-empty -m "Deploy ${GITHUB_SHA}
Deploy of commit https://github.com/phil-opp/blog_os/commit/${GITHUB_SHA}"
working-directory: "blog_os_deploy"
- name: "Show Changes"
run: "git show"
working-directory: "blog_os_deploy"
- name: "Push Changes"
run: "git push"
working-directory: "blog_os_deploy"

View File

@@ -1,27 +0,0 @@
name: Check Links
on:
push:
branches:
- "*"
- "!staging.tmp"
tags:
- "*"
pull_request:
schedule:
- cron: "0 0 1/4 * *" # every 4 days
jobs:
zola_check:
name: "Zola Link Check"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: "Download Zola"
run: curl -sL https://github.com/getzola/zola/releases/download/v0.19.0/zola-v0.19.0-x86_64-unknown-linux-gnu.tar.gz | tar zxv
- name: "Run zola check"
run: ../zola check
working-directory: "blog"

View File

@@ -1,33 +0,0 @@
name: Build code on schedule
on:
schedule:
- cron: '40 1 * * *' # every day at 1:40
jobs:
trigger-build:
name: Trigger Build
strategy:
matrix:
branch: [
post-01,
post-02,
post-03,
post-04,
post-05,
post-06,
post-07,
post-08,
post-09,
post-10,
post-11,
post-12,
]
runs-on: ubuntu-latest
steps:
- name: Invoke workflow
uses: benc-uk/workflow-dispatch@v1.1
with:
workflow: Code
token: ${{ secrets.SCHEDULED_BUILDS_TOKEN }}
ref: ${{ matrix.branch }}

15
.gitignore vendored
View File

@@ -1 +1,14 @@
code
# Compiled files
*.o
*.so
*.rlib
*.dll
# Executables
*.exe
# Generated by Cargo
/target/
# Build directory
/build/

19
.travis.yml Normal file
View File

@@ -0,0 +1,19 @@
language: rust
rust:
- nightly
sudo: false
notifications:
email:
on_success: never
on_failure: change
addons:
apt:
packages:
- nasm
script:
- make

21
Cargo.toml Normal file
View File

@@ -0,0 +1,21 @@
[package]
name = "blog_os"
version = "0.1.0"
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
[lib]
crate-type = ["staticlib"]
[dependencies]
rlibc = "*"
spin = "*"
[dependencies.bitflags]
git = "https://github.com/phil-opp/bitflags.git"
branch = "no_std"
[dependencies.multiboot2]
git = "https://github.com/phil-opp/multiboot2-elf64"
[dependencies.allocator]
path = "src/memory/alloc/allocator"

View File

@@ -1,6 +1,6 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
@@ -174,3 +174,28 @@
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,9 +0,0 @@
The MIT License (MIT)
Copyright (c) 2019 Philipp Oppermann
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

55
Makefile Normal file
View File

@@ -0,0 +1,55 @@
# Copyright 2015 Philipp Oppermann
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
arch ?= x86_64
kernel := build/kernel-$(arch).bin
iso := build/os-$(arch).iso
rust_os := target/debug/libblog_os.a
linker_script := src/arch/$(arch)/linker.ld
grub_cfg := src/arch/$(arch)/grub.cfg
assembly_source_files := $(wildcard src/arch/$(arch)/*.asm)
assembly_object_files := $(patsubst src/arch/$(arch)/%.asm, \
build/arch/$(arch)/%.o, $(assembly_source_files))
.PHONY: all clean run iso cargo
all: $(kernel)
clean:
@cargo clean
@rm -rf build
run: $(iso)
@qemu-system-x86_64 -s -hda $(iso)
iso: $(iso)
$(iso): $(kernel)
@mkdir -p build/isofiles/boot/grub
@cp $(kernel) build/isofiles/boot/
@cp $(grub_cfg) build/isofiles/boot/grub
@grub-mkrescue -o $(iso) build/isofiles 2> /dev/null
@rm -r build/isofiles
$(kernel): cargo $(rust_os) $(assembly_object_files) $(linker_script)
@ld -n --gc-sections -T $(linker_script) -o $(kernel) $(assembly_object_files) $(rust_os)
cargo:
@cargo rustc -- -Z no-landing-pads
# compile assembly files
build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm
@mkdir -p $(shell dirname $@)
@nasm -felf64 $< -o $@

132
README.md
View File

@@ -1,131 +1 @@
# Blog OS
This repository contains the source code for the _Writing an OS in Rust_ series at [os.phil-opp.com](https://os.phil-opp.com).
If you have questions, open an issue or chat with us [on Gitter](https://gitter.im/phil-opp/blog_os).
## Where is the code?
The code for each post lives in a separate git branch. This makes it possible to see the intermediate state after each post.
**The code for the latest post is available [here][latest-post].**
[latest-post]: https://github.com/phil-opp/blog_os/tree/post-12
You can find the branch for each post by following the `(source code)` link in the [post list](#posts) below. The branches are named `post-XX` where `XX` is the post number, for example `post-03` for the _VGA Text Mode_ post or `post-07` for the _Hardware Interrupts_ post. For build instructions, see the Readme of the respective branch.
You can check out a branch in a subdirectory using [git worktree]:
[git worktree]: https://git-scm.com/docs/git-worktree
```
git worktree add code post-10
```
The above command creates a subdirectory named `code` that contains the code for the 10th post ("Heap Allocation").
## Posts
The goal of this project is to provide step-by-step tutorials in individual blog posts. We currently have the following set of posts:
**Bare Bones:**
- [A Freestanding Rust Binary](https://os.phil-opp.com/freestanding-rust-binary/)
([source code](https://github.com/phil-opp/blog_os/tree/post-01))
- [A Minimal Rust Kernel](https://os.phil-opp.com/minimal-rust-kernel/)
([source code](https://github.com/phil-opp/blog_os/tree/post-02))
- [VGA Text Mode](https://os.phil-opp.com/vga-text-mode/)
([source code](https://github.com/phil-opp/blog_os/tree/post-03))
- [Testing](https://os.phil-opp.com/testing/)
([source code](https://github.com/phil-opp/blog_os/tree/post-04))
**Interrupts:**
- [CPU Exceptions](https://os.phil-opp.com/cpu-exceptions/)
([source code](https://github.com/phil-opp/blog_os/tree/post-05))
- [Double Faults](https://os.phil-opp.com/double-fault-exceptions/)
([source code](https://github.com/phil-opp/blog_os/tree/post-06))
- [Hardware Interrupts](https://os.phil-opp.com/hardware-interrupts/)
([source code](https://github.com/phil-opp/blog_os/tree/post-07))
**Memory Management:**
- [Introduction to Paging](https://os.phil-opp.com/paging-introduction/)
([source code](https://github.com/phil-opp/blog_os/tree/post-08))
- [Paging Implementation](https://os.phil-opp.com/paging-implementation/)
([source code](https://github.com/phil-opp/blog_os/tree/post-09))
- [Heap Allocation](https://os.phil-opp.com/heap-allocation/)
([source code](https://github.com/phil-opp/blog_os/tree/post-10))
- [Allocator Designs](https://os.phil-opp.com/allocator-designs/)
([source code](https://github.com/phil-opp/blog_os/tree/post-11))
**Multitasking**:
- [Async/Await](https://os.phil-opp.com/async-await/)
([source code](https://github.com/phil-opp/blog_os/tree/post-12))
## First Edition Posts
The current version of the blog is already the second edition. The first edition is outdated and no longer maintained, but might still be useful. The posts of the first edition are:
<details><summary><i>Click to expand</i></summary>
**Bare Bones:**
- [A Minimal x86 Kernel](https://os.phil-opp.com/multiboot-kernel.html)
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_1))
- [Entering Long Mode](https://os.phil-opp.com/entering-longmode.html)
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_2))
- [Set Up Rust](https://os.phil-opp.com/set-up-rust.html)
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_3))
- [Printing to Screen](https://os.phil-opp.com/printing-to-screen.html)
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_4))
**Memory Management:**
- [Allocating Frames](https://os.phil-opp.com/allocating-frames.html)
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_5))
- [Page Tables](https://os.phil-opp.com/modifying-page-tables.html)
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_6))
- [Remap the Kernel](https://os.phil-opp.com/remap-the-kernel.html)
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_7))
- [Kernel Heap](https://os.phil-opp.com/kernel-heap.html)
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_8))
**Exceptions:**
- [Handling Exceptions](https://os.phil-opp.com/handling-exceptions.html)
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_9))
- [Double Faults](https://os.phil-opp.com/double-faults.html)
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_10))
**Additional Resources:**
- [Cross Compile Binutils](https://os.phil-opp.com/cross-compile-binutils.html)
- [Cross Compile libcore](https://os.phil-opp.com/cross-compile-libcore.html)
- [Set Up GDB](https://os.phil-opp.com/set-up-gdb)
- [Handling Exceptions using Naked Functions](https://os.phil-opp.com/handling-exceptions-with-naked-fns.html)
- [Catching Exceptions](https://os.phil-opp.com/catching-exceptions.html)
([source code](https://github.com/phil-opp/blog_os/tree/catching_exceptions))
- [Better Exception Messages](https://os.phil-opp.com/better-exception-messages.html)
([source code](https://github.com/phil-opp/blog_os/tree/better_exception_messages))
- [Returning from Exceptions](https://os.phil-opp.com/returning-from-exceptions.html)
([source code](https://github.com/phil-opp/blog_os/tree/returning_from_exceptions))
</details>
## License
This project, with exception of the `blog/content` folder, is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
https://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
at your option.
For licensing of the `blog/content` folder, see the [`blog/content/README.md`](blog/content/README.md).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
# blogOS

2
blog/.gitignore vendored
View File

@@ -1,2 +0,0 @@
/public
zola

View File

@@ -1,89 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import io
import urllib
import datetime
from github import Github
g = Github()
one_month_ago = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=32)
def filter_date(issue):
return issue.closed_at > one_month_ago
def format_number(number):
if number > 1000:
return u"{:.1f}k".format(float(number) / 1000)
else:
return u"{}".format(number)
with io.open("templates/auto/recent-updates.html", 'w', encoding='utf8') as recent_updates:
recent_updates.truncate()
relnotes_issues = g.search_issues("is:merged", repo="phil-opp/blog_os", type="pr", label="relnotes")[:100]
recent_relnotes_issues = list(filter(filter_date, relnotes_issues))
if len(recent_relnotes_issues) == 0:
recent_updates.write(u"No notable updates recently.")
else:
recent_updates.write(u"<ul>\n")
for pr in sorted(recent_relnotes_issues, key=lambda issue: issue.closed_at, reverse=True):
link = '<a href="' + pr.html_url + '">' + pr.title + "</a> "
iso_date = pr.closed_at.isoformat()
readable_date = pr.closed_at.strftime("%b&nbsp;%d")
datetime_str = '<time datetime="' + iso_date + '">' + readable_date + '</time>'
recent_updates.write(u" <li>" + link + datetime_str + "</li>\n")
recent_updates.write(u"</ul>")
repo = g.get_repo("phil-opp/blog_os")
with io.open("templates/auto/stars.html", 'w', encoding='utf8') as stars:
stars.truncate()
stars.write(format_number(repo.stargazers_count))
with io.open("templates/auto/forks.html", 'w', encoding='utf8') as forks:
forks.truncate()
forks.write(format_number(repo.forks_count))
# query "This week in Rust OSDev posts"
lines = []
year = 2020
month = 4
while True:
url = "https://rust-osdev.com/this-month/" + str(year) + "-" + str(month).zfill(2) + "/"
try:
urllib.request.urlopen(url)
except urllib.error.HTTPError as e:
break
month_str = datetime.date(1900, month, 1).strftime('%B')
link = '<a href="' + url + '">This Month in Rust OSDev (' + month_str + " " + str(year) + ")</a> "
lines.append(u" <li><b>" + link + "</b></li>\n")
month = month + 1
if month > 12:
month = 1
year = year + 1
lines.reverse()
with io.open("templates/auto/status-updates.html", 'w', encoding='utf8') as status_updates:
status_updates.truncate()
for line in lines:
status_updates.write(line)
with io.open("templates/auto/status-updates-truncated.html", 'w', encoding='utf8') as status_updates:
status_updates.truncate()
for index, line in enumerate(lines):
if index == 5:
break
status_updates.write(line)

View File

@@ -1,281 +0,0 @@
base_url = "https://os.phil-opp.com"
title = "Writing an OS in Rust"
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
generate_feeds = true
feed_filenames = ["rss.xml"]
compile_sass = true
minify_html = false
ignored_content = ["*/README.md", "*/LICENSE-CC-BY-NC"]
[markdown]
highlight_code = true
highlight_theme = "visual-studio-dark"
smart_punctuation = true
[link_checker]
skip_prefixes = [
"https://crates.io/crates", # see https://github.com/rust-lang/crates.io/issues/788
"https://www.amd.com/system/files/TechDocs/", # seems to have problems with PDFs
"https://developer.apple.com/library/archive/qa/qa1118/_index.html", # results in a 401 (I don't know why)
"https://github.com", # rate limiting often leads to "Error 429 Too Many Requests"
"https://www.linkedin.com/", # seems to send invalid HTTP status codes
]
skip_anchor_prefixes = [
"https://github.com/", # see https://github.com/getzola/zola/issues/805
"https://docs.rs/x86_64/0.1.2/src/", # source code highlight
"https://doc.rust-jp.rs/book-ja/", # seems like Zola has problems with Japanese anchor names
"https://doc.rust-jp.rs/edition-guide/rust-2018", # seems like Zola has problems with Japanese anchor names
"https://doc.rust-jp.rs/rust-nomicon-ja/", # seems like Zola has problems with Japanese anchor names
]
[extra]
subtitle = "Philipp Oppermann's blog"
author = { name = "Philipp Oppermann" }
default_language = "en"
languages = ["en", "zh-CN", "zh-TW", "fr", "ja", "fa", "ru", "ko", "ar", "es"]
[translations]
lang_name = "English (original)"
toc = "Table of Contents"
all_posts = "« All Posts"
comments = "Comments"
comments_notice = "Please leave your comments in English if possible."
readmore = "read&nbsp;more&nbsp;»"
not_translated = "(This post is not translated yet.)"
translated_content = "Translated Content:"
translated_content_notice = "This is a community translation of the <strong><a href=\"_original.permalink_\">_original.title_</a></strong> post. It might be incomplete, outdated or contain errors. Please report any issues!"
translated_by = "Translation by"
translation_contributors = "With contributions from"
word_separator = "and"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# Chinese (simplified)
[languages.zh-CN]
title = "Writing an OS in Rust"
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
[languages.zh-CN.translations]
lang_name = "Chinese (simplified)"
toc = "目录"
all_posts = "« 所有文章"
comments = "评论"
comments_notice = "请尽可能使用英语评论。"
readmore = "更多 »"
not_translated = "(该文章还没有被翻译。)"
translated_content = "翻译内容:"
translated_content_notice = "这是对原文章 <strong><a href=\"_original.permalink_\">_original.title_</a></strong> 的社区中文翻译。它可能不完整,过时或者包含错误。可以在 <a href=\"https://github.com/phil-opp/blog_os/issues/961\">这个 Issue</a> 上评论和提问!"
translated_by = "翻译者:"
translation_contributors = "With contributions from"
word_separator = "和"
support_me = """
<h2>支持我</h2>
<p>创建和维护这个博客以及相关的库带来了十分庞大的工作量,即便我十分热爱它们,仍然需要你们的支持。通过赞助我,可以让我有能投入更多时间与精力在创造新内容,开发新功能上。赞助我最好的办法是通过<a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. 十分感谢各位!</p>
"""
comment_note = """
你有问题需要解决,想要分享反馈,或者讨论更多的想法吗?请随时在这里留下评论!请使用尽量使用英文并遵循 Rust 的 <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. 这个讨论串将与 <a href="_discussion_url_"><em>discussion on GitHub</em></a> 直接连接,所以你也可以直接在那边发表评论
"""
# Chinese (traditional)
[languages.zh-TW]
title = "Writing an OS in Rust"
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
[languages.zh-TW.translations]
lang_name = "Chinese (traditional)"
toc = "目錄"
all_posts = "« 所有文章"
comments = "評論"
comments_notice = "請儘可能使用英語評論。"
readmore = "更多 »"
not_translated = "(該文章還沒有被翻譯。)"
translated_content = "翻譯內容:"
translated_content_notice = "這是對原文章 <strong><a href=\"_original.permalink_\">_original.title_</a></strong> 的社區中文翻譯。它可能不完整,過時或者包含錯誤。可以在 <a href=\"https://github.com/phil-opp/blog_os/issues/961\">這個 Issue</a> 上評論和提問!"
translated_by = "翻譯者:"
translation_contributors = "With contributions from"
word_separator = "和"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# Japanese
[languages.ja]
title = "Writing an OS in Rust"
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
[languages.ja.translations]
lang_name = "Japanese"
toc = "目次"
all_posts = "« すべての記事へ"
comments = "コメント"
comments_notice = "可能な限りコメントは英語で残すようにしてください。"
readmore = "もっと読む »"
not_translated = "(この記事はまだ翻訳されていません。)"
translated_content = "この記事は翻訳されたものです:"
translated_content_notice = "この記事は<strong><a href=\"_original.permalink_\">_original.title_</a></strong>をコミュニティの手により翻訳したものです。そのため、翻訳が完全・最新でなかったり、原文にない誤りを含んでいる可能性があります。問題があれば<a href=\"https://github.com/phil-opp/blog_os/issues/906\">このissue</a>上で報告してください!"
translated_by = "翻訳者:"
translation_contributors = "With contributions from"
word_separator = "及び"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# Persian
[languages.fa]
title = "Writing an OS in Rust"
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
[languages.fa.translations]
lang_name = "Persian"
toc = "فهرست مطالب"
all_posts = "« همه پست‌ها"
comments = "نظرات"
comments_notice = "لطفا نظرات خود را در صورت امکان به انگلیسی بنویسید."
readmore = "ادامه‌مطلب»"
not_translated = "(.این پست هنوز ترجمه نشده است)"
translated_content = "محتوای ترجمه شده:"
translated_content_notice = "این یک ترجمه از جامعه کاربران برای پست <strong><a href=\"_original.permalink_\">_original.title_</a></strong> است. ممکن است ناقص، منسوخ شده یا دارای خطا باشد. لطفا هر گونه مشکل را در <a href=\"https://github.com/phil-opp/blog_os/issues/908\">این ایشو</a> گزارش دهید!"
translated_by = "ترجمه توسط"
translation_contributors = "With contributions from"
word_separator = "و"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# Russian
[languages.ru]
title = "Writing an OS in Rust"
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
[languages.ru.translations]
lang_name = "Russian"
toc = "Содержание"
all_posts = Все посты"
comments = "Комментарии"
comments_notice = "Пожалуйста, оставляйте комментарии на английском по возможности."
readmore = "читать&nbsp;дальше&nbsp;»"
not_translated = "(Этот пост еще не переведен.)"
translated_content = "Переведенное содержание:"
translated_content_notice = "Это перевод сообщества поста <strong><a href=\"_original.permalink_\">_original.title_</a></strong>. Он может быть неполным, устаревшим или содержать ошибки. Пожалуйста, сообщайте о любых проблемах!"
translated_by = "Перевод сделан"
translation_contributors = "With contributions from"
word_separator = "и"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# French
[languages.fr]
title = "Writing an OS in Rust"
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
[languages.fr.translations]
lang_name = "French"
toc = "Table des matières"
all_posts = "« Tous les articles"
comments = "Commentaires"
comments_notice = "Veuillez commenter en Anglais si possible."
readmore = "Voir&nbsp;plus&nbsp;»"
not_translated = "(Cet article n'est pas encore traduit.)"
translated_content = "Contenu traduit : "
translated_content_notice = "Ceci est une traduction communautaire de l'article <strong><a href=\"_original.permalink_\">_original.title_</a></strong>. Il peut être incomplet, obsolète ou contenir des erreurs. Veuillez signaler les quelconques problèmes !"
translated_by = "Traduit par : "
translation_contributors = "With contributions from"
word_separator = "et"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# Korean
[languages.ko]
title = "Writing an OS in Rust"
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
[languages.ko.translations]
lang_name = "Korean"
toc = "목차"
all_posts = "« 모든 게시글"
comments = "댓글"
comments_notice = "댓글은 가능하면 영어로 작성해주세요."
readmore = "더&nbsp;읽기&nbsp;»"
not_translated = "(아직 번역이 완료되지 않은 게시글입니다)"
translated_content = "번역된 내용 : "
translated_content_notice = "이것은 커뮤니티 멤버가 <strong><a href=\"_original.permalink_\">_original.title_</a></strong> 포스트를 번역한 글입니다. 부족한 설명이나 오류, 혹은 시간이 지나 더 이상 유효하지 않은 정보를 발견하시면 제보해주세요!"
translated_by = "번역한 사람 : "
translation_contributors = "With contributions from"
word_separator = "와"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
[languages.ar]
title = "Writing an OS in Rust"
[languages.ar.translations]
lang_name = "Arabic"
toc = "Table of Contents"
all_posts = "« All Posts"
comments = "Comments"
comments_notice = "Please leave your comments in English if possible."
readmore = "read&nbsp;more&nbsp;»"
not_translated = "(This post is not translated yet.)"
translated_content = "Translated Content:"
translated_content_notice = "This is a community translation of the <strong><a href=\"_original.permalink_\">_original.title_</a></strong> post. It might be incomplete, outdated or contain errors. Please report any issues!"
translated_by = "Translation by"
translation_contributors = "With contributions from"
word_separator = "and"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# Spanish
[languages.es]
title = "Writing an OS in Rust"
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
[languages.es.translations]
lang_name = "Spanish"
toc = "Tabla de Contenidos"
all_posts = "« Todos los Posts"
comments = "Comentarios"
comments_notice = "Por favor deja tus comentarios en inglés si es posible."
readmore = "leer&nbsp;más&nbsp;»"
not_translated = "(Este post aún no está traducido.)"
translated_content = "Contenido Traducido:"
translated_content_notice = "Esta es una traducción comunitaria del post <strong><a href=\"_original.permalink_\">_original.title_</a></strong>. Puede estar incompleta, desactualizada o contener errores. ¡Por favor reporta cualquier problema!"
translated_by = "Traducción por"
translation_contributors = "Con contribuciones de"
word_separator = "y"
support_me = """
<h2>Apóyame</h2>
<p>Crear y mantener este blog y las bibliotecas asociadas es mucho trabajo, pero realmente disfruto haciéndolo. Al apoyarme, me permites invertir más tiempo en nuevo contenido, nuevas características y mantenimiento continuo. La mejor manera de apoyarme es <a href=\"https://github.com/sponsors/phil-opp\"><em>patrocinarme en GitHub</em></a>. ¡Gracias!</p>
"""
comment_note = """
¿Tienes algún problema, quieres compartir comentarios o discutir más ideas? ¡No dudes en dejar un comentario aquí! Por favor, utiliza inglés y sigue el <a href=\"https://www.rust-lang.org/policies/code-of-conduct\">código de conducta</a> de Rust. Este hilo de comentarios se vincula directamente con una <a href=\"_discussion_url_\"><em>discusión en GitHub</em></a>, así que también puedes comentar allí si lo prefieres.
"""

View File

@@ -1,96 +0,0 @@
Creative Commons Attribution-NonCommercial 4.0 International Public License
By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
Section 1 Definitions.
Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
Licensor means the individual(s) or entity(ies) granting rights under this Public License.
NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.
Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
Section 2 Scope.
License grant.
Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and
produce, reproduce, and Share Adapted Material for NonCommercial purposes only.
Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
Term. The term of this Public License is specified in Section 6(a).
Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
Downstream recipients.
Offer from the Licensor Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
Other rights.
Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
Patent and trademark rights are not licensed under this Public License.
To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.
Section 3 License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the following conditions.
Attribution.
If You Share the Licensed Material (including in modified form), You must:
retain the following if it is supplied by the Licensor with the Licensed Material:
identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
a copyright notice;
a notice that refers to this Public License;
a notice that refers to the disclaimer of warranties;
a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License.
Section 4 Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;
if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and
You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
Section 5 Disclaimer of Warranties and Limitation of Liability.
Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
Section 6 Term and Termination.
This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
Section 7 Other Terms and Conditions.
The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
Section 8 Interpretation.
For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.

View File

@@ -1,19 +0,0 @@
# Blog Content
This folder contains the content for the _"Writing an OS in Rust"_ blog.
## License
This folder is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License, available in [LICENSE-CC-BY-NC](LICENSE-CC-BY-NC) or under <https://creativecommons.org/licenses/by-nc/4.0/>.
All _code examples_ between markdown code blocks denoted by three backticks (<code>\`\`\`</code>) are additionally licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](../../LICENSE-APACHE) or
https://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](../../LICENSE-MIT) or https://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed as above, without any additional terms or conditions.

View File

@@ -1,12 +0,0 @@
+++
template = "edition-2/index.html"
+++
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">كتابة نظام تشغيل بلغة Rust </h1>
<div class="front-page-introduction">
تنشئ سلسلة المدونات هذه نظام تشغيل صغير بلغة البرمجة [Rust ](https://www.rust-lang.org/). كل منشور هو عبارة عن برنامج تعليمي صغير ويتضمن كل الشيفرة المطلوبة، لذا يمكنك المتابعة إذا أردت. الكود المصدري متاح أيضًا في مستودع [Github ](https://github.com/phil-opp/blog_os) المقابل.
آخر منشور: <!-- latest-post -->
</div>

View File

@@ -1,13 +0,0 @@
+++
template = "edition-2/index.html"
+++
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Escribiendo un sistema operativo en Rust</h1>
<div class="front-page-introduction">
Esta serie de blogs crea un pequeño sistema operativo en el [lenguaje de programación Rust](https://www.rust-lang.org/). Cada publicación es un pequeño tutorial e incluye todo el código necesario, para que puedas seguir los pasos si lo deseas. El código fuente también está disponible en el [repositorio correspondiente de Github](https://github.com/phil-opp/blog_os).
Última publicación: <!-- latest-post -->
</div>

View File

@@ -1,13 +0,0 @@
+++
template = "edition-2/index.html"
+++
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">نوشتن یک سیستم عامل با راست</h1>
<div class="front-page-introduction right-to-left">
این مجموعه بلاگ یک سیستم عامل کوچک در [زبان برنامه نویسی Rust](https://www.rust-lang.org/) ایجاد می کند. هر پست یک آموزش کوچک است و شامل تمام کدهای مورد نیاز است ، بنابراین اگر دوست دارید می توانید آن را دنبال کنید. کد منبع نیز در [مخزن گیت‌هاب](https://github.com/phil-opp/blog_os) مربوطه موجود است.
اخرین پست: <!-- latest-post -->
</div>

View File

@@ -1,13 +0,0 @@
+++
template = "edition-2/index.html"
+++
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Écrire un OS en Rust</h1>
<div class="front-page-introduction">
L'objectif de ce blog est de créer un petit système d'exploitation avec le [langage de programmation Rust](https://www.rust-lang.org/). Chaque article est un petit tutoriel et comprend tout le code nécessaire, vous pouvez donc essayer en même temps si vous le souhaitez. Le code source est aussi disponible dans le [dépôt GitHub](https://github.com/phil-opp/blog_os) correspondant.
Dernier article : <!-- latest-post -->
</div>

View File

@@ -1,13 +0,0 @@
+++
template = "edition-2/index.html"
+++
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">RustでOSを書く</h1>
<div class="front-page-introduction">
このブログシリーズでは、ちょっとしたオペレーティングシステムを[Rustプログラミング言語](https://www.rust-lang.org/)を使って作ります。それぞれの記事が小さなチュートリアルになっており、必要なコードも全て記事内に記されているので、一つずつ読み進めて行けば理解できるでしょう。対応した[Githubリポジトリ](https://github.com/phil-opp/blog_os)でソースコードを見ることもできます。
最新記事: <!-- latest-post -->
</div>

View File

@@ -1,14 +0,0 @@
+++
template = "edition-2/index.html"
+++
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Rust로 OS 구현하기</h1>
<div class="front-page-introduction">
이 블로그 시리즈는 [Rust 프로그래밍 언어](https://www.rust-lang.org/)로 작은 OS를 구현하는 것을 주제로 합니다.
각 포스트는 구현에 필요한 소스 코드를 포함한 작은 튜토리얼 형식으로 구성되어 있습니다. 소스 코드는 이 블로그의 [Github 저장소](https://github.com/phil-opp/blog_os)에서도 확인하실 수 있습니다.
최신 포스트: <!-- latest-post -->
</div>

View File

@@ -1,13 +0,0 @@
+++
template = "edition-2/index.html"
+++
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Writing an OS in Rust</h1>
<div class="front-page-introduction">
This blog series creates a small operating system in the [Rust programming language](https://www.rust-lang.org/). Each post is a small tutorial and includes all needed code, so you can follow along if you like. The source code is also available in the corresponding [Github repository](https://github.com/phil-opp/blog_os).
Latest post: <!-- latest-post -->
</div>

View File

@@ -1,13 +0,0 @@
+++
template = "edition-2/index.html"
+++
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Собственная операционная система на Rust</h1>
<div class="front-page-introduction">
Этот блог посвящен написанию маленькой операционной системы на [языке программирования Rust](https://www.rust-lang.org/). Каждый пост &mdash; это маленькое руководство, включающее в себя весь необходимый код, &mdash; вы сможете следовать ему, если пожелаете. Исходный код также доступен в соотвестующем [репозитории на Github](https://github.com/phil-opp/blog_os).
Последний пост: <!-- latest-post -->
</div>

View File

@@ -1,13 +0,0 @@
+++
template = "edition-2/index.html"
+++
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">用Rust写一个操作系统</h1>
<div class="front-page-introduction">
这个博客系列用[Rust编程语言](https://www.rust-lang.org/)编写了一个小操作系统。每篇文章都是一个小教程,并且包含了所有代码,你可以跟着一起学习。源代码也放在了[Github 仓库](https://github.com/phil-opp/blog_os)。
最新文章: <!-- latest-post -->
</div>

View File

@@ -1,13 +0,0 @@
+++
template = "edition-2/index.html"
+++
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Writing an OS in Rust</h1>
<div class="front-page-introduction">
This blog series creates a small operating system in the [Rust programming language](https://www.rust-lang.org/). Each post is a small tutorial and includes all needed code, so you can follow along if you like. The source code is also available in the corresponding [Github repository](https://github.com/phil-opp/blog_os).
Latest post: <!-- latest-post -->
</div>

View File

@@ -1,5 +0,0 @@
+++
title = "First Edition"
template = "edition-1/index.html"
aliases = ["first-edition/index.html"]
+++

View File

@@ -1,6 +0,0 @@
+++
title = "Extra Content"
insert_anchor_links = "left"
render = false
sort_by = "weight"
+++

View File

@@ -1,51 +0,0 @@
+++
title = "Cross Compile Binutils"
template = "plain.html"
path = "cross-compile-binutils"
weight = 2
+++
The [GNU Binutils] are a collection of various binary tools such as `ld`, `as`, `objdump`, or `readelf`. These tools are platform-specific, so you need to compile them again if your host system and target system are different. In our case, we need `ld` and `objdump` for the x86_64 architecture.
[GNU Binutils]: https://www.gnu.org/software/binutils/
## Building Setup
First, you need to download a current binutils version from [here][download] \(the latest one is near the bottom). After extracting, you should have a folder named `binutils-2.X` where `X` is for example `25.1`. Now can create and switch to a new folder for building (recommended):
[download]: ftp://sourceware.org/pub/binutils/snapshots
```bash
mkdir build-binutils
cd build-binutils
```
## Configuration
We execute binutils's `configure` and pass a lot of arguments to it (replace the `X` with the version number):
```bash
../binutils-2.X/configure --target=x86_64-elf --prefix="$HOME/opt/cross" \
--disable-nls --disable-werror \
--disable-gdb --disable-libdecnumber --disable-readline --disable-sim
```
- The `target` argument specifies the the x86_64 target architecture.
- The `prefix` argument selects the installation directory, you can change it if you like. But be careful that you do not overwrite your system's binutils.
- The `disable-nls` flag disables native language support (so you'll get the same english error messages). It also reduces build dependencies.
- The `disable-werror` turns all warnings into errors.
- The last line disables features we don't need to reduce compile time.
## Building it
Now we can build and install it to the location supplied as `prefix` (it will take a while):
```bash
make
make install
```
Now you should have multiple `x86_64-elf-XXX` files in `$HOME/opt/cross/bin`.
## Adding it to the PATH
To use the tools from the command line easily, you should add the `bin` folder to your PATH:
```bash
export PATH="$HOME/opt/cross/bin:$PATH"
```
If you add this line to your e.g. `.bashrc`, the `x86_64-elf-XXX` commands are always available.

View File

@@ -1,36 +0,0 @@
+++
title = "Cross Compiling: libcore"
template = "plain.html"
path = "cross-compile-libcore"
weight = 3
+++
If you get an `error: can't find crate for 'core'`, you're probably compiling for a different target (e.g. you're passing the `target` option to `cargo build`). Now the compiler complains that it can't find the `core` library. This document gives a quick overview how to fix this problem. For more details, see the [rust-cross] project.
[rust-cross]: https://github.com/japaric/rust-cross
## Libcore
The core library is a dependency-free library that is added implicitly when using `#![no_std]`. It provides basic standard library features like Option or Iterator. The core library is installed together with the rust compiler (just like the std library). But the installed libcore is specific to your architecture. If you aren't working on x86_64 Linux and pass `target x86_64unknownlinuxgnu` to cargo, it can't find a x86_64 libcore. To fix this, you can either use `rustup` or `xargo`.
## rustup
Thanks to [rustup], cross-compilation for [official target triples] is pretty easy today: Just execute `rustup target add x86_64-unknown-linux-gnu`.
[rustup]: https://rustup.rs
[official target triples]: https://github.com/japaric/rust-cross#the-target-triple
## xargo
If you're using a _custom target specification_, the `rustup` method doesn't work. Instead, you can use [xargo]. Xargo is a wrapper for cargo that eases cross compilation. We can install it by executing:
```
cargo install xargo
```
If the installation fails, make sure that you have `cmake` and the OpenSSL headers installed. For more details, see the xargo's [dependency section].
[xargo]: https://github.com/japaric/xargo
[dependency section]: https://github.com/japaric/xargo#dependencies
Xargo is “a drop-in replacement for cargo”, so every cargo command also works with `xargo`. You can do e.g. `xargo --help`, `xargo clean`, or `xargo doc`. However, the `build` command gains additional functionality: `xargo build` will automatically cross compile the `core` library (and a few other libraries such as `alloc`) when compiling for custom targets.
[xargo]: https://github.com/japaric/xargo
So if your custom target file is named `your-cool-target.json`, you can compile your code using xargo through `xargo build --target your-cool-target` (note the omitted extension).

View File

@@ -1,562 +0,0 @@
+++
title = "Catching Exceptions"
weight = 1
path = "catching-exceptions"
aliases = ["catching-exceptions.html"]
date = 2016-05-28
template = "edition-1/page.html"
[extra]
updated = "2016-06-25"
+++
In this post, we start exploring exceptions. We set up an interrupt descriptor table and add handler functions. At the end of this post, our kernel will be able to catch divide-by-zero faults.
<!-- more -->
As always, the complete source code is on [GitHub]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
[GitHub]: https://github.com/phil-opp/blog_os/tree/catching_exceptions
[issues]: https://github.com/phil-opp/blog_os/issues
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
[“Handling Exceptions”]: @/edition-1/posts/09-handling-exceptions/index.md
## Exceptions
An exception signals that something is wrong with the current instruction. For example, the CPU issues an exception if the current instruction tries to divide by 0. When an exception occurs, the CPU interrupts its current work and immediately calls a specific exception handler function, depending on the exception type.
We've already seen several types of exceptions in our kernel:
- **Invalid Opcode**: This exception occurs when the current instruction is invalid. For example, this exception occurred when we tried to use SSE instructions before enabling SSE. Without SSE, the CPU didn't know the `movups` and `movaps` instructions, so it throws an exception when it stumbles over them.
- **Page Fault**: A page fault occurs on illegal memory accesses. For example, if the current instruction tries to read from an unmapped page or tries to write to a read-only page.
- **Double Fault**: When an exception occurs, the CPU tries to call the corresponding handler function. If another exception exception occurs _while calling the exception handler_, the CPU raises a double fault exception. This exception also occurs when there is no handler function registered for an exception.
- **Triple Fault**: If an exception occurs while the CPU tries to call the double fault handler function, it issues a fatal _triple fault_. We can't catch or handle a triple fault. Most processors react by resetting themselves and rebooting the operating system. This causes the bootloops we experienced in the previous posts.
For the full list of exceptions check out the [OSDev wiki][exceptions].
[exceptions]: https://wiki.osdev.org/Exceptions
### The Interrupt Descriptor Table
In order to catch and handle exceptions, we have to set up a so-called _Interrupt Descriptor Table_ (IDT). In this table we can specify a handler function for each CPU exception. The hardware uses this table directly, so we need to follow a predefined format. Each entry must have the following 16-byte structure:
Type| Name | Description
----|--------------------------|-----------------------------------
u16 | Function Pointer [0:15] | The lower bits of the pointer to the handler function.
u16 | GDT selector | Selector of a code segment in the GDT.
u16 | Options | (see below)
u16 | Function Pointer [16:31] | The middle bits of the pointer to the handler function.
u32 | Function Pointer [32:63] | The remaining bits of the pointer to the handler function.
u32 | Reserved |
The options field has the following format:
Bits | Name | Description
------|-----------------------------------|-----------------------------------
0-2 | Interrupt Stack Table Index | 0: Don't switch stacks, 1-7: Switch to the n-th stack in the Interrupt Stack Table when this handler is called.
3-7 | Reserved |
8 | 0: Interrupt Gate, 1: Trap Gate | If this bit is 0, interrupts are disabled when this handler is called.
9-11 | must be one |
12 | must be zero |
1314 | Descriptor Privilege Level (DPL) | The minimal privilege level required for calling this handler.
15 | Present |
Each exception has a predefined IDT index. For example the invalid opcode exception has table index 6 and the page fault exception has table index 14. Thus, the hardware can automatically load the corresponding IDT entry for each exception. The [Exception Table][exceptions] in the OSDev wiki shows the IDT indexes of all exceptions in the “Vector nr.” column.
When an exception occurs, the CPU roughly does the following:
1. Read the corresponding entry from the Interrupt Descriptor Table (IDT). For example, the CPU reads the 14-th entry when a page fault occurs.
2. Check if the entry is present. Raise a double fault if not.
3. Push some registers on the stack, including the instruction pointer and the [EFLAGS] register. (We will use these values in a future post.)
4. Disable interrupts if the entry is an interrupt gate (bit 40 not set).
5. Load the specified GDT selector into the CS segment.
6. Jump to the specified handler function.
[EFLAGS]: https://en.wikipedia.org/wiki/FLAGS_register
## Handling Exceptions
Let's try to catch and handle CPU exceptions. We start by creating a new `interrupts` module with an `idt` submodule:
``` rust
// in src/lib.rs
...
mod interrupts;
...
```
``` rust
// src/interrupts/mod.rs
mod idt;
```
Now we create types for the IDT and its entries:
```rust
// src/interrupts/idt.rs
use x86_64::instructions::segmentation;
use x86_64::structures::gdt::SegmentSelector;
use x86_64::PrivilegeLevel;
pub struct Idt([Entry; 16]);
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct Entry {
pointer_low: u16,
gdt_selector: SegmentSelector,
options: EntryOptions,
pointer_middle: u16,
pointer_high: u32,
reserved: u32,
}
```
The IDT is variable sized and can have up to 256 entries. We only need the first 16 entries in this post, so we define the table as `[Entry; 16]`. The remaining 240 handlers are treated as non-present by the CPU.
The `Entry` type is the translation of the above table to Rust. The `repr(C, packed)` attribute ensures that the compiler keeps the field ordering and does not add any padding between them. Instead of describing the `gdt_selector` as a plain `u16`, we use the `SegmentSelector` type of the `x86` crate. We also merge bits 32 to 47 into an `option` field, because Rust has no `u3` or `u1` type. The `EntryOptions` type is described below:
### Entry Options
The `EntryOptions` type has the following skeleton:
``` rust
#[derive(Debug, Clone, Copy)]
pub struct EntryOptions(u16);
impl EntryOptions {
fn new() -> Self {...}
pub fn set_present(&mut self, present: bool) {...}
pub fn disable_interrupts(&mut self, disable: bool) {...}
pub fn set_privilege_level(&mut self, dpl: u16) {...}
pub fn set_stack_index(&mut self, index: u16) {...}
}
```
The implementations of these methods need to modify the correct bits of the `u16` without touching the other bits. For example, we would need the following bit-fiddling to set the stack index:
``` rust
self.0 = (self.0 & 0xfff8) | stack_index;
```
Or alternatively:
``` rust
self.0 = (self.0 & (!0b111)) | stack_index;
```
Or:
``` rust
self.0 = ((self.0 >> 3) << 3) | stack_index;
```
Well, none of these variants is really _readable_ and it's very easy to make mistakes somewhere. Therefore I created a `BitField` trait that provides the following [Range]-based API:
[Range]: https://doc.rust-lang.org/nightly/core/ops/struct.Range.html
``` rust
self.0.set_bits(0..3, stack_index);
```
I think it is much more readable, since we abstracted away all bit-masking details. The `BitField` trait is contained in the [bit_field] crate. (It's pretty new, so it might still contain bugs.) To add it as dependency, we run `cargo add bit_field` and add `extern crate bit_field;` to our `src/lib.rs`.
[bit_field]: https://crates.io/crates/bit_field
Now we can use the trait to implement the methods of `EntryOptions`:
```rust
// in src/interrupts/idt.rs
use bit_field::BitField;
#[derive(Debug, Clone, Copy)]
pub struct EntryOptions(u16);
impl EntryOptions {
fn minimal() -> Self {
let mut options = 0;
options.set_bits(9..12, 0b111); // 'must-be-one' bits
EntryOptions(options)
}
fn new() -> Self {
let mut options = Self::minimal();
options.set_present(true).disable_interrupts(true);
options
}
pub fn set_present(&mut self, present: bool) -> &mut Self {
self.0.set_bit(15, present);
self
}
pub fn disable_interrupts(&mut self, disable: bool) -> &mut Self {
self.0.set_bit(8, !disable);
self
}
pub fn set_privilege_level(&mut self, dpl: u16) -> &mut Self {
self.0.set_bits(13..15, dpl);
self
}
pub fn set_stack_index(&mut self, index: u16) -> &mut Self {
self.0.set_bits(0..3, index);
self
}
}
```
Note that the ranges are _exclusive_ the upper bound. The `minimal` function creates an `EntryOptions` type with only the “must-be-one” bits set. The `new` function, on the other hand, chooses reasonable defaults: It sets the present bit (why would you want to create a non-present entry?) and disables interrupts (normally we don't want that our exception handlers can be interrupted). By returning the self pointer from the `set_*` methods, we allow easy method chaining such as `options.set_present(true).disable_interrupts(true)`.
### Creating IDT Entries
Now we can add a function to create new IDT entries:
```rust
impl Entry {
fn new(gdt_selector: SegmentSelector, handler: HandlerFunc) -> Self {
let pointer = handler as u64;
Entry {
gdt_selector: gdt_selector,
pointer_low: pointer as u16,
pointer_middle: (pointer >> 16) as u16,
pointer_high: (pointer >> 32) as u32,
options: EntryOptions::new(),
reserved: 0,
}
}
}
```
We take a GDT selector and a handler function as arguments and create a new IDT entry for it. The `HandlerFunc` type is described below. It is a function pointer that can be converted to an `u64`. We choose the lower 16 bits for `pointer_low`, the next 16 bits for `pointer_middle` and the remaining 32 bits for `pointer_high`. For the options field we choose our default options, i.e. present and disabled interrupts.
### The Handler Function Type
The `HandlerFunc` type is a type alias for a function type:
``` rust
pub type HandlerFunc = extern "C" fn() -> !;
```
It needs to be a function with a defined [calling convention], as it called directly by the hardware. The C calling convention is the de facto standard in OS development, so we're using it, too. The function takes no arguments, since the hardware doesn't supply any arguments when jumping to the handler function.
[calling convention]: https://en.wikipedia.org/wiki/Calling_convention
It is important that the function is [diverging], i.e. it must never return. The reason is that the hardware doesn't _call_ the handler functions, it just _jumps_ to them after pushing some values to the stack. So our stack might look different:
[diverging]: https://doc.rust-lang.org/rust-by-example/fn/diverging.html
![normal function return vs interrupt function return](normal-vs-interrupt-function-return.svg)
If our handler function returned normally, it would try to pop the return address from the stack. But it might get some completely different value then. For example, the CPU pushes an error code for some exceptions. Bad things would happen if we interpreted this error code as return address and jumped to it. Therefore interrupt handler functions must diverge[^fn-must-diverge].
[^fn-must-diverge]: Another reason is that we overwrite the current register values by executing the handler function. Thus, the interrupted function looses its state and can't proceed anyway.
### IDT methods
Let's add a function to create new interrupt descriptor tables:
```rust
impl Idt {
pub fn new() -> Idt {
Idt([Entry::missing(); 16])
}
}
impl Entry {
fn missing() -> Self {
Entry {
gdt_selector: SegmentSelector::new(0, PrivilegeLevel::Ring0),
pointer_low: 0,
pointer_middle: 0,
pointer_high: 0,
options: EntryOptions::minimal(),
reserved: 0,
}
}
}
```
The `missing` function creates a non-present Entry. We could choose any values for the pointer and GDT selector fields as long as the present bit is not set.
However, a table with non-present entries is not very useful. So we create a `set_handler` method to add new handler functions:
```rust
impl Idt {
pub fn set_handler(&mut self, entry: u8, handler: HandlerFunc)
-> &mut EntryOptions
{
self.0[entry as usize] = Entry::new(segmentation::cs(), handler);
&mut self.0[entry as usize].options
}
}
```
The method overwrites the specified entry with the given handler function. We use the `segmentation::cs` function of the [x86_64 crate] to get the current code segment descriptor. There's no need for different kernel code segments in long mode, so the current `cs` value should be always the right choice.
[x86_64 crate]: https://docs.rs/x86_64
By returning a mutual reference to the entry's options, we allow the caller to override the default settings. For example, the caller could add a non-present entry by executing: `idt.set_handler(11, handler_fn).set_present(false)`.
### Loading the IDT
Now we're able to create new interrupt descriptor tables with registered handler functions. We just need a way to load an IDT, so that the CPU uses it. The x86 architecture uses a special register to store the active IDT and its length. In order to load a new IDT we need to update this register through the [lidt] instruction.
[lidt]: https://www.felixcloutier.com/x86/lgdt:lidt
The `lidt` instruction expects a pointer to a special data structure, which specifies the start address of the IDT and its length:
Type | Name | Description
--------|---------|-----------------------------------
u16 | Limit | The maximum addressable byte in the table. Equal to the table size in bytes minus 1.
u64 | Offset | Virtual start address of the table.
This structure is already contained [in the x86_64 crate], so we don't need to create it ourselves. The same is true for the [lidt function]. So we just need to put the pieces together to create a `load` method:
[in the x86_64 crate]: https://docs.rs/x86_64/0.1.0/x86_64/instructions/tables/struct.DescriptorTablePointer.html
[lidt function]: https://docs.rs/x86_64/0.1.0/x86_64/instructions/tables/fn.lidt.html
```rust
impl Idt {
pub fn load(&self) {
use x86_64::instructions::tables::{DescriptorTablePointer, lidt};
use core::mem::size_of;
let ptr = DescriptorTablePointer {
base: self as *const _ as u64,
limit: (size_of::<Self>() - 1) as u16,
};
unsafe { lidt(&ptr) };
}
}
```
The method does not need to modify the IDT, so it takes `self` by immutable reference. First, we create a `DescriptorTablePointer` and then we pass it to `lidt`. The `lidt` function expects that the `base` field has the type `u64`, therefore we need to cast the `self` pointer. For calculating the `limit` we use [mem::size_of]. The additional `-1` is needed because the limit field has to be the maximum addressable byte (inclusive bound). We need an unsafe block around `lidt`, because the function assumes that the specified handler addresses are valid.
[mem::size_of]: https://doc.rust-lang.org/nightly/core/mem/fn.size_of.html
#### Safety
But can we really guarantee that handler addresses are always valid? Let's see:
- The `Idt::new` function creates a new table populated with non-present entries. There's no way to set these entries to present from outside of this module, so this function is fine.
- The `set_handler` method allows us to overwrite a specified entry and point it to some handler function. Rust's type system guarantees that function pointers are always valid (as long as no `unsafe` is involved), so this function is fine, too.
There are no other public functions in the `idt` module (except `load`), so it should be safe… right?
Wrong! Imagine the following scenario:
```rust
pub fn init() {
load_idt();
cause_page_fault();
}
fn load_idt() {
let mut idt = idt::Idt::new();
idt.set_handler(14, page_fault_handler);
idt.load();
}
fn cause_page_fault() {
let x = [1,2,3,4,5,6,7,8,9];
unsafe{ *(0xdeadbeaf as *mut u64) = x[4] };
}
```
This won't work. If we're lucky, we get a triple fault and a boot loop. If we're unlucky, our kernel does strange things and fails at some completely unrelated place. So what's the problem here?
Well, we construct an IDT _on the stack_ and load it. It is perfectly valid until the end of the `load_idt` function. But as soon as the function returns, its stack frame can be reused by other functions. Thus, the IDT gets overwritten by the stack frame of the `cause_page_fault` function. So when the page fault occurs and the CPU tries to read the entry, it only sees some garbage values and issues a double fault, which escalates to a triple fault and a CPU reset.
Now imagine that the `cause_page_fault` function declared an array of pointers instead. If the present was coincidentally set, the CPU would jump to some random pointer and interpret random memory as code. This would be a clear violation of memory safety.
#### Fixing the load method
So how do we fix it? We could make the load function itself `unsafe` and push the unsafety to the caller. However, there is a much better solution in this case. In order to see it, we formulate the requirement for the `load` method:
> The referenced IDT must be valid until a new IDT is loaded.
We can't know when the next IDT will be loaded. Maybe never. So in the worst case:
> The referenced IDT must be valid as long as our kernel runs.
This is exactly the definition of a [static lifetime]. So we can easily ensure that the IDT lives long enough by adding a `'static` requirement to the signature of the `load` function:
[static lifetime]: https://doc.rust-lang.org/rust-by-example/scope/lifetime/static_lifetime.html
```rust
pub fn load(&'static self) {...}
// ^^^^^^^ ensure that the IDT reference has the 'static lifetime
```
That's it! Now the Rust compiler ensures that the above error can't happen anymore:
```
error: `idt` does not live long enough
--> src/interrupts/mod.rs:78:5
78 |> idt.load();
|> ^^^
note: reference must be valid for the static lifetime...
note: ...but borrowed value is only valid for the block suffix following
statement 0 at 75:34
--> src/interrupts/mod.rs:75:35
75 |> let mut idt = idt::Idt::new();
|> ^
```
### A static IDT
So a valid IDT needs to have the `'static` lifetime. We can either create a `static` IDT or [deliberately leak a Box][into_raw]. We will most likely only need a single IDT for the foreseeable future, so let's try the `static` approach:
[into_raw]: https://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html#method.into_raw
```rust
// in src/interrupts/mod.rs
static IDT: idt::Idt = {
let mut idt = idt::Idt::new();
idt.set_handler(0, divide_by_zero_handler);
idt
};
extern "C" fn divide_by_zero_handler() -> ! {
println!("EXCEPTION: DIVIDE BY ZERO");
loop {}
}
```
We register a single handler function for a [divide by zero error] \(index 0). Like the name says, this exception occurs when dividing a number by 0. Thus we have an easy way to test our new exception handler.
[divide by zero error]: https://wiki.osdev.org/Exceptions#Division_Error
However, it doesn't work this way:
```
error: calls in statics are limited to constant functions, struct and enum
constructors [E0015]
...
error: blocks in statics are limited to items and tail expressions [E0016]
...
error: references in statics may only refer to immutable values [E0017]
...
```
The reason is that the Rust compiler is not able to evaluate the value of the `static` at compile time. Maybe it will work someday when `const` functions become more powerful. But until then, we have to find another solution.
#### Lazy Statics to the Rescue
Fortunately the `lazy_static` macro exists. Instead of evaluating a `static` at compile time, the macro performs the initialization when the `static` is referenced the first time. Thus, we can do almost everything in the initialization block and are even able to read runtime values.
Let's add the `lazy_static` crate to our project:
```rust
// in src/lib.rs
#[macro_use]
extern crate lazy_static;
```
```toml
# in Cargo.toml
[dependencies.lazy_static]
version = "0.2.1"
features = ["spin_no_std"]
```
We need the `spin_no_std` feature, since we don't link the standard library.
With `lazy_static`, we can define our IDT without problems:
```rust
// in src/interrupts/mod.rs
lazy_static! {
static ref IDT: idt::Idt = {
let mut idt = idt::Idt::new();
idt.set_handler(0, divide_by_zero_handler);
idt
};
}
```
Now we're ready to load our IDT! Therefore we add a `interrupts::init` function:
```rust
// in src/interrupts/mod.rs
pub fn init() {
IDT.load();
}
```
We don't need our `assert_has_not_been_called` macro here, since nothing bad happens when `init` is called twice. It just reloads the same IDT again.
## Testing it
Now we should be able to catch page faults! Let's try it in our `rust_main`:
```rust
// in src/lib.rs
pub extern "C" fn rust_main(...) {
...
memory::init(boot_info);
// initialize our IDT
interrupts::init();
// provoke a divide-by-zero fault
42 / 0;
println!("It did not crash!");
loop {}
}
```
When we run it, we get a runtime panic:
```
PANIC in src/lib.rs at line 57:
attempted to divide by zero
```
That's a not our exception handler. The reason is that Rust itself checks for a possible division by zero and panics in that case. So in order to raise a divide-by-zero error in the CPU, we need to bypass the Rust compiler somehow.
### Inline Assembly
In order to cause a divide-by-zero exception, we need to execute a [div] or [idiv] assembly instruction with operand 0. We could write a small assembly function and call it from our Rust code. An easier way is to use Rust's [inline assembly] macro.
[div]: https://www.felixcloutier.com/x86/div
[idiv]: https://www.felixcloutier.com/x86/idiv
[inline assembly]: https://doc.rust-lang.org/1.10.0/book/inline-assembly.html
Inline assembly allows us to write raw x86 assembly within a Rust function. The feature is unstable, so we need to add `#![feature(asm)]` to our `src/lib.rs`. Then we're able to write a `divide_by_zero` function:
```rust
fn divide_by_zero() {
unsafe {
asm!("mov dx, 0; div dx" ::: "ax", "dx" : "volatile", "intel")
}
}
```
Let's try to decode it:
- The `asm!` macro emits raw assembly instructions, so it's `unsafe` to use it.
- We insert two assembly instructions here: `mov dx, 0` and `div dx`. The former loads a 0 into the `dx` register (a subset of `rdx`) and the latter divides the `ax` register by `dx`. (The `div` instruction always implicitly operates on the `ax` register).
- The colons are separators. After the first `:` we could specify output operands and after the second `:` we could specify input operands. We need neither, so we leave these areas empty.
- After the third colon, we specify the so-called _clobbers_. These tell the compiler that our assembly modifies the values of some registers. Otherwise, the compiler assumes that the registers preserve their value. In our case, we clobber `dx` (we load 0 to it) and `ax` (the `div` instruction places the result in it).
- The last block (after the 4th colon) specifies some options. The `volatile` option tells the compiler: “This code has side effects. Do not delete it and do not move it elsewhere”. In our case, the “side effect” is the divide-by-zero exception. Finally, the `intel` option allows us to use the Intel assembly syntax instead of the default AT&T syntax.
Let's use our new `divide_by_zero` function to raise a CPU exception:
```rust
// in src/lib.rs
pub extern "C" fn rust_main(...) {
...
// provoke a divide-by-zero fault
divide_by_zero();
println!("It did not crash!");
loop {}
}
```
It works! We see a `EXCEPTION: DIVIDE BY ZERO` message at the bottom of our screen:
![QEMU screenshot with `EXCEPTION: DIVIDE BY ZERO` message](qemu-divide-error-println.png)
## What's next?
We've successfully caught our first exception! However, our `EXCEPTION: DIVIDE BY ZERO` message doesn't contain much information about the cause of the exception. The next post improves the situation by printing i.a. the current stack pointer and address of the causing instruction. We will also explore other exceptions such as page faults, for which the CPU pushes an _error code_ on the stack.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,664 +0,0 @@
+++
title = "Better Exception Messages"
weight = 2
path = "better-exception-messages"
aliases = ["better-exception-messages.html"]
date = 2016-08-03
template = "edition-1/page.html"
[extra]
updated = "2016-11-01"
+++
In this post, we explore exceptions in more detail. Our goal is to print additional information when an exception occurs, for example the values of the instruction and stack pointer. In the course of this, we will explore inline assembly and naked functions. We will also add a handler function for page faults and read the associated error code.
<!-- more -->
As always, the complete source code is on [GitHub]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a [gitter chat] and a comment section at the end of this page.
[GitHub]: https://github.com/phil-opp/blog_os/tree/better_exception_messages
[issues]: https://github.com/phil-opp/blog_os/issues
[gitter chat]: https://gitter.im/phil-opp/blog_os
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
[“Handling Exceptions”]: @/edition-1/posts/09-handling-exceptions/index.md
## Exceptions in Detail
An exception signals that something is wrong with the currently-executed instruction. Whenever an exception occurs, the CPU interrupts its current work and starts an internal exception routine.
This routine involves reading the interrupt descriptor table and invoking the registered handler function. But first, the CPU pushes various information onto the stack, which describe the current state and provide information about the cause of the exception:
![exception stack frame](exception-stack-frame.svg)
The pushed information contain the instruction and stack pointer, the current CPU flags, and (for some exceptions) an error code, which contains further information about the cause of the exception. Let's look at the fields in detail:
- First, the CPU aligns the stack pointer on a 16-byte boundary. This allows the handler function to use SSE instructions, which partly expect such an alignment.
- After that, the CPU pushes the stack segment descriptor (SS) and the old stack pointer (from before the alignment) onto the stack. This allows us to restore the previous stack pointer when we want to resume the interrupted program.
- Then the CPU pushes the contents of the [RFLAGS] register. This register contains various state information of the interrupted program. For example, it indicates if interrupts were enabled and whether the last executed instruction returned zero.
- Next the CPU pushes the instruction pointer and its code segment descriptor onto the stack. This tells us the address of the last executed instruction, which caused the exception.
- Finally, the CPU pushes an error code for some exceptions. This error code only exists for exceptions such as page faults or general protection faults and provides additional information. For example, it tells us whether a page fault was caused by a read or a write request.
[RFLAGS]: https://en.wikipedia.org/wiki/FLAGS_register
## Printing the Exception Stack Frame
Let's create a struct that represents the exception stack frame:
```rust
// in src/interrupts/mod.rs
#[derive(Debug)]
#[repr(C)]
struct ExceptionStackFrame {
instruction_pointer: u64,
code_segment: u64,
cpu_flags: u64,
stack_pointer: u64,
stack_segment: u64,
}
```
The divide-by-zero fault pushes no error code, so we leave it out for now. Note that the stack grows downwards in memory, so we need to declare the fields in reverse order (compared to the figure above).
Now we need a way to find the memory address of this stack frame. When we look at the above graphic again, we see that the start address of the exception stack frame is the new stack pointer. So we just need to read the value of `rsp` at the very beginning of our handler function:
```rust
// in src/interrupts/mod.rs
extern "C" fn divide_by_zero_handler() -> ! {
let stack_frame: &ExceptionStackFrame;
unsafe {
asm!("mov $0, rsp" : "=r"(stack_frame) ::: "intel");
}
println!("\nEXCEPTION: DIVIDE BY ZERO\n{:#?}", stack_frame);
loop {}
}
```
We're using [inline assembly] here to load the value from the `rsp` register into `stack_frame`. The syntax is a bit strange, so here's a quick explanation:
[inline assembly]: https://doc.rust-lang.org/1.10.0/book/inline-assembly.html
- The `asm!` macro emits raw assembly instructions. This is the only way to read raw register values in Rust.
- We insert a single assembly instruction: `mov $0, rsp`. It moves the value of `rsp` to some register (the `$0` is a placeholder for an arbitrary register, which gets filled by the compiler).
- The colons are separators. After the first colon, the `asm!` macro expects output operands. We're specifying our `stack_frame` variable as a single output operand here. The `=r` tells the compiler that it should use any register for the first placeholder `$0`.
- After the second colon, we can specify input operands. We don't need any, therefore we leave it empty.
- After the third colon, the macro expects so called [clobbers]. We don't change any register values, so we leave it empty too.
- The last block (after the 4th colon) specifies options. The `intel` option tells the compiler that our code is in Intel assembly syntax (instead of the default AT&T syntax).
[clobbers]: https://doc.rust-lang.org/1.10.0/book/inline-assembly.html#clobbers
So the inline assembly loads the stack pointer value to `stack_frame` at the very beginning of our function. Thus we have a pointer to the exception stack frame and are able to pretty-print its `Debug` formatting through the `{:#?}` argument.
### Testing it
Let's try it by executing `make run`:
![qemu printing an ExceptionStackFrame with strange values](qemu-print-stack-frame-try.png)
Those `ExceptionStackFrame` values look very wrong. The instruction pointer definitely shouldn't be 1 and the code segment should be `0x8` instead of some big number. So what's going on here?
### Debugging
It seems like we somehow got the pointer wrong. The `ExceptionStackFrame` type and our inline assembly seem correct, so something must be modifying `rsp` before we load it into `stack_frame`.
Let's see what's happening by looking at the disassembly of our function:
```
> objdump -d build/kernel-x86_64.bin | grep -A20 "divide_by_zero_handler"
[...]
000000000010ced0 <_ZN7blog_os10interrupts22divide_by_zero_handler17h62189e8E>:
10ced0: 55 push %rbp
10ced1: 48 89 e5 mov %rsp,%rbp
10ced4: 48 81 ec b0 00 00 00 sub $0xb0,%rsp
10cedb: 48 8d 45 98 lea -0x68(%rbp),%rax
10cedf: 48 b9 1d 1d 1d 1d 1d movabs $0x1d1d1d1d1d1d1d1d,%rcx
10cee6: 1d 1d 1d
10cee9: 48 89 4d 98 mov %rcx,-0x68(%rbp)
10ceed: 48 89 4d f8 mov %rcx,-0x8(%rbp)
10cef1: 48 89 e1 mov %rsp,%rcx
10cef4: 48 89 4d f8 mov %rcx,-0x8(%rbp)
10cef8: ...
[...]
```
Our `divide_by_zero_handler` starts at address `0x10ced0`. Let's look at the instruction at address `0x10cef1`:
```
mov %rsp,%rcx
```
This is our inline assembly instruction, which loads the stack pointer into the `stack_frame` variable. It just looks a bit different, since it's in AT&T syntax and contains `rcx` instead of our `$0` placeholder. It moves `rsp` to `rcx`, and then the next instruction (`mov %rcx,-0x8(%rbp)`) moves `rcx` to the variable on the stack.
We can clearly see the problem here: The compiler inserted various other instructions before our inline assembly. These instructions modify the stack pointer so that we don't read the original `rsp` value and get a wrong pointer. But why is the compiler doing this?
The reason is that we need some place on the stack to store things like variables. Therefore the compiler inserts a so-called _[function prologue]_, which prepares the stack and reserves space for all variables. In our case, the compiler subtracts from the stack pointer to make room for i.a. our `stack_frame` variable. This prologue is the first thing in every function and comes before every other code.
So in order to correctly load the exception frame pointer, we need some way to circumvent the automatic prologue generation.
[function prologue]: https://en.wikipedia.org/wiki/Function_prologue
### Naked Functions
Fortunately there is a way to disable the prologue: [naked functions]. A naked function has no prologue and immediately starts with the first instruction of its body. However, most Rust code requires the prologue. Therefore naked functions should only contain inline assembly.
[naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
A naked function looks like this (note the `#[naked]` attribute):
```rust
#[naked]
extern "C" fn naked_function_example() {
unsafe {
asm!("mov rax, 0x42" ::: "rax" : "intel");
};
}
```
Naked functions are highly unstable, so we need to add `#![feature(naked_functions)]` to our `src/lib.rs`.
If you want to try it, insert it in `src/lib.rs` and call it from `rust_main`. When we inspect the disassembly, we see that the function prologue is missing:
```
> objdump -d build/kernel-x86_64.bin | grep -A5 "naked_function_example"
[...]
000000000010df90 <_ZN7blog_os22naked_function_example17ha9f733dfe42b595dE>:
10df90: 48 c7 c0 2a 00 00 00 mov $0x42,%rax
10df97: c3 retq
10df98: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
10df9f: 00
```
It contains just the specified inline assembly and a return instruction (you can ignore the junk values after the return statement). So let's try to use a naked function to retrieve the exception frame pointer.
### A Naked Exception Handler
We can't use Rust code in naked functions, but we still want to use Rust in our exception handler. Therefore we split our handler function in two parts. A main exception handler in Rust and a small naked wrapper function, which just loads the exception frame pointer and then calls the main handler.
Our new two-stage exception handler looks like this:
```rust
// in src/interrupts/mod.rs
#[naked]
extern "C" fn divide_by_zero_wrapper() -> ! {
unsafe {
asm!(/* load exception frame pointer and call main handler */);
}
}
extern "C" fn divide_by_zero_handler(stack_frame: &ExceptionStackFrame)
-> !
{
println!("\nEXCEPTION: DIVIDE BY ZERO\n{:#?}",
unsafe { &*stack_frame });
loop {}
}
```
The naked wrapper function retrieves the exception stack frame pointer and then calls the `divide_by_zero_handler` with the pointer as argument. We can't use Rust code in naked functions, so we need to do both things in inline assembly.
Retrieving the pointer to the exception stack frame is easy: We just need to load it from the `rsp` register. Our wrapper function has no prologue (it's naked), so we can be sure that nothing modifies the register before.
Calling the main handler is a bit more complicated, since we need to pass the argument correctly. Our main handler uses the C calling convention, which specifies that the the first argument is passed in the `rdi` register. So we need to load the pointer value into `rdi` and then use the `call` instruction to call `divide_by_zero_handler`.
Translated to assembly, it looks like this:
```nasm
mov rdi, rsp
call divide_by_zero_handler
```
It moves the exception stack frame pointer from `rsp` to `rdi`, where the first argument is expected, and then calls the main handler. Let's create the corresponding inline assembly to complete our wrapper function:
```rust
#[naked]
extern "C" fn divide_by_zero_wrapper() -> ! {
unsafe {
asm!("mov rdi, rsp; call $0"
:: "i"(divide_by_zero_handler as extern "C" fn(_) -> !)
: "rdi" : "intel");
}
}
```
Instead of `call divide_by_zero_handler`, we use a placeholder again. The reason is Rust's name mangling, which changes the name of the `divide_by_zero_handler` function. To circumvent this, we pass a function pointer as input parameter (after the second colon). The `"i"` tells the compiler that it is an immediate value, which can be directly inserted for the placeholder. We also specify a clobber after the third colon, which tells the compiler that we change the value of the `rdi` register.
### Intrinsics::Unreachable
When we try to compile it, we get the following error:
```
error: computation may converge in a function marked as diverging
--> src/interrupts/mod.rs:23:1
|>
23 |> extern "C" fn divide_by_zero_wrapper() -> ! {
|> ^
```
The reason is that we marked our `divide_by_zero_wrapper` function as diverging (the `!`). We call another diverging function in inline assembly, so it is clear that the function diverges. However, the Rust compiler doesn't understand inline assembly, so it doesn't know that. To fix this, we tell the compiler that all code after the `asm!` macro is unreachable:
```rust
#[naked]
extern "C" fn divide_by_zero_wrapper() -> ! {
unsafe {
asm!("mov rdi, rsp; call $0"
:: "i"(divide_by_zero_handler as extern "C" fn(_) -> !)
: "rdi" : "intel");
::core::intrinsics::unreachable();
}
}
```
The [intrinsics::unreachable] function is unstable, so we need to add `#![feature(core_intrinsics)]` to our `src/lib.rs`. It is just an annotation for the compiler and produces no real code. (Not to be confused with the [unreachable!] macro, which is completely different!)
[intrinsics::unreachable]: https://doc.rust-lang.org/nightly/core/intrinsics/fn.unreachable.html
[unreachable!]: https://doc.rust-lang.org/nightly/core/macro.unreachable!.html
### It works!
The last step is to update the interrupt descriptor table (IDT) to use our new wrapper function:
```rust
// in src/interrupts/mod.rs
lazy_static! {
static ref IDT: idt::Idt = {
let mut idt = idt::Idt::new();
idt.set_handler(0, divide_by_zero_wrapper); // changed
idt
};
}
```
Now we see a correct exception stack frame when we execute `make run`:
![QEMU showing correct divide by zero stack frame](qemu-divide-by-zero-stack-frame.png)
## Testing on real Hardware
Virtual machines such as QEMU are very convenient to quickly test our kernel. However, they might behave a bit different than real hardware in some situations. So we should test our kernel on real hardware, too.
Let's do it by burning it to an USB stick:
```
> sudo dd if=build/os-x86_64.iso of=/dev/sdX; and sync
```
Replace `sdX` by the device name of your USB stick. But **be careful**! The command will erase everything on that device.
Now we should be able to boot from this USB stick. When we do it, we see that it works fine on real hardware, too. Great!
However, this section wouldn't exist if there weren't a problem. To trigger this problem, we add some example code to the start of our `divide_by_zero_handler`:
```rust
// in src/interrupts/mod.rs
extern "C" fn divide_by_zero_handler(...) {
let x = (1u64, 2u64, 3u64);
let y = Some(x);
for i in (0..100).map(|z| (z, z - 1)) {}
println!(...);
loop {}
}
```
This is just some garbage code that doesn't do anything useful. When we try it in QEMU using `make run`, it still works fine. However, when we burn it to an USB stick again and boot from it on real hardware, we see that our computer reboots just before printing the exception message.
So our code, which worked well in QEMU, _causes a triple fault_ on real hardware. What's happening?
### Reproducing the Bug in QEMU
Debugging on a real machine is difficult. Fortunately there is a way to reproduce this bug in QEMU: We use Linux's [Kernel-based Virtual Machine] \(KVM) by passing the `enable-kvm` flag:
[Kernel-based Virtual Machine]: https://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine
```
> qemu-system-x86_64 -cdrom build/os-x86_64.iso -enable-kvm
```
Now QEMU triple faults as well. This should make debugging much easier.
### Debugging
QEMU's `-d int`, which prints every exception, doesn't seem to work in KVM mode. However `-d cpu_reset` still works. It prints the complete CPU state whenever the CPU resets. Let's try it:
```
> qemu-system-x86_64 -cdrom build/os-x86_64.iso -enable-kvm -d cpu_reset
CPU Reset (CPU 0)
EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000
EIP=00000000 EFL=00000000 [-------] CPL=0 II=0 A20=0 SMM=0 HLT=0
[...]
CPU Reset (CPU 0)
EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000663
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000
EIP=0000fff0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
[...]
CPU Reset (CPU 0)
RAX=0000000000118cb8 RBX=0000000000000800 RCX=1d1d1d1d1d1d1d1d RDX=0..0000000
RSI=0000000000112cd0 RDI=0000000000118d38 RBP=0000000000118d28 RSP=0..0118c68
R8 =0000000000000000 R9 =0000000000000100 R10=0000000000118700 R11=0..0118a00
R12=0000000000000000 R13=0000000000000000 R14=0000000000000000 R15=0..0000000
RIP=000000000010cf08 RFL=00210002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
[...]
```
The first two resets occur while the CPU is still in 32-bit mode (`EAX` instead of `RAX`), so we ignore them. The third reset is the interesting one, because it occurs in 64-bit mode. The register dump tells us that the instruction pointer (`rip`) was `0x10cf08` just before the reset. This might be the address of the instruction that caused the triple fault.
We can find the corresponding instruction by disassembling our kernel:
```
objdump -d build/kernel-x86_64.bin | grep "10cf08:"
10cf08: 0f 29 45 b0 movaps %xmm0,-0x50(%rbp)
```
The [movaps] instruction is an [SSE] instruction that moves aligned 128bit values. It can fail for a number of reasons:
[movaps]: https://www.felixcloutier.com/x86/movaps
[SSE]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
1. For an illegal memory operand effective address in the CS, DS, ES, FS or GS segments.
2. For an illegal address in the SS segment.
3. If a memory operand is not aligned on a 16-byte boundary.
4. For a page fault.
5. If TS in CR0 is set.
The segment registers contain no meaningful values in long mode, so they can't contain illegal addresses. We did not change the TS bit in [CR0] and there is no reason for a page fault either. So it has to be option 3.
[CR0]: https://en.wikipedia.org/wiki/Control_register#CR0
### 16-byte Alignment
Some SSE instructions such as `movaps` require that memory operands are 16-byte aligned. In our case, the instruction is `movaps %xmm0,-0x50(%rbp)`, which writes to address `rbp - 0x50`. Therefore `rbp` needs to be 16-byte aligned.
Let's look at the above `-d cpu_reset` dump again and check the value of `rbp`:
```
CPU Reset (CPU 0)
RAX=[...] RBX=[...] RCX=[...] RDX=[...]
RSI=[...] RDI=[...] RBP=0000000000118d28 RSP=[...]
...
```
`RBP` is `0x118d28`, which is _not_ 16-byte aligned. So this is the reason for the triple fault. (It seems like QEMU doesn't check the alignment for `movaps`, but real hardware of course does.)
But how did we end up with a misaligned `rbp` register?
### The Base Pointer
In order to solve this mystery, we need to look at the disassembly of the preceding code:
```
> objdump -d build/kernel-x86_64.bin | grep -B10 "10cf08:"
000000000010cee0 <_ZN7blog_os10interrupts22divide_by_zero_handler17hE>:
10cee0: 55 push %rbp
10cee1: 48 89 e5 mov %rsp,%rbp
10cee4: 48 81 ec c0 00 00 00 sub $0xc0,%rsp
10ceeb: 48 8d 45 90 lea -0x70(%rbp),%rax
10ceef: 48 b9 1d 1d 1d 1d 1d movabs $0x1d1d1d1d1d1d1d1d,%rcx
10cef6: 1d 1d 1d
10cef9: 48 89 4d 90 mov %rcx,-0x70(%rbp)
10cefd: 48 89 7d f8 mov %rdi,-0x8(%rbp)
10cf01: 0f 10 05 a8 51 00 00 movups 0x51a8(%rip),%xmm0
10cf08: 0f 29 45 b0 movaps %xmm0,-0x50(%rbp)
```
At the last line we have the `movaps` instruction, which caused the triple fault. The exception occurs inside our `divide_by_zero_handler` function. We see that `rbp` is loaded with the value of `rsp` at the beginning (at `0x10cee1`). The `rbp` register holds the so-called _base pointer_, which points to the beginning of the stack frame. It is used in the rest of the function to address variables and other values on the stack.
The base pointer is initialized directly from the stack pointer (`rsp`) after pushing the old base pointer. There is no special alignment code, so the compiler blindly assumes that `(rsp - 8)`[^fn-rsp-8] is always 16-byte aligned. This seems to be wrong in our case. But why does the compiler assume this?
[^fn-rsp-8]: By pushing the old base pointer, `rsp` is updated to `rsp-8`.
### Calling Conventions
The reason is that our exception handler is defined as `extern "C" function`, which specifies that it's using the C [calling convention]. On x86_64 Linux, the C calling convention is specified by the System V AMD64 ABI ([PDF][system v abi]). Section 3.2.2 defines the following:
[calling convention]: https://en.wikipedia.org/wiki/X86_calling_conventions
[system v abi]: https://web.archive.org/web/20160801075139/https://www.x86-64.org/documentation/abi.pdf
> The end of the input argument area shall be aligned on a 16 byte boundary. In other words, the value (%rsp + 8) is always a multiple of 16 when control is transferred to the function entry point.
The “end of the input argument area” refers to the last stack-passed argument (in our case there aren't any). So the stack pointer must be 16 byte aligned whenever we `call` a C-compatible function. The `call` instruction then pushes the return value on the stack so that “the value (%rsp + 8) is a multiple of 16 when control is transferred to the function entry point”.
_Summary_: The calling convention requires a 16 byte aligned stack pointer before `call` instructions. The compiler relies on this requirement, but we broke it somehow. Thus the generated code triple faults due to a misaligned memory address in the `movaps` instruction.
### Fixing the Alignment
In order to fix this bug, we need to make sure that the stack pointer is correctly aligned before calling `extern "C"` functions. Let's summarize the stack pointer modifications that occur before the exception handler is called:
1. The CPU aligns the stack pointer to a 16 byte boundary.
2. The CPU pushes `ss`, `rsp`, `rflags`, `cs`, and `rip`. So it pushes five 8 byte registers, which makes `rsp` misaligned.
3. The wrapper function calls `divide_by_zero_handler` with a misaligned stack pointer.
The problem is that we're pushing an uneven number of 8 byte registers. Thus we need to align the stack pointer again before the `call` instruction:
```rust
#[naked]
extern "C" fn divide_by_zero_wrapper() -> ! {
unsafe {
asm!("mov rdi, rsp
sub rsp, 8 // align the stack pointer
call $0"
:: "i"(divide_by_zero_handler as extern "C" fn(_) -> !)
: "rdi" : "intel");
::core::intrinsics::unreachable();
}
}
```
The additional `sub rsp, 8` instruction aligns the stack pointer to a 16 byte boundary. Now it should work on real hardware (and in QEMU KVM mode) again.
## A Handler Macro
The next step is to add handlers for other exceptions. However, we would need wrapper functions for them too. To avoid this code duplication, we create a `handler` macro that creates the wrapper functions for us:
```rust
// in src/interrupts/mod.rs
macro_rules! handler {
($name: ident) => {{
#[naked]
extern "C" fn wrapper() -> ! {
unsafe {
asm!("mov rdi, rsp
sub rsp, 8 // align the stack pointer
call $0"
:: "i"($name as extern "C" fn(
&ExceptionStackFrame) -> !)
: "rdi" : "intel");
::core::intrinsics::unreachable();
}
}
wrapper
}}
}
```
The macro takes a single Rust identifier (`ident`) as argument and expands to a `{}` block (hence the double braces). The block defines a new wrapper function that calls the function `$name` and passes a pointer to the exception stack frame. Note that we're fixing the argument type to `&ExceptionStackFrame`. If we used a `_` like before, the passed function could accept an arbitrary argument, which would lead to ugly bugs at runtime.
Now we can remove the `divide_by_zero_wrapper` and use our new `handler!` macro instead:
```rust
// in src/interrupts/mod.rs
lazy_static! {
static ref IDT: idt::Idt = {
let mut idt = idt::Idt::new();
idt.set_handler(0, handler!(divide_by_zero_handler)); // new
idt
};
}
```
Note that the `handler!` macro needs to be defined above the static `IDT`, because macros are only available after their definition.
### Invalid Opcode Exception
With the `handler!` macro we can create new handler functions easily. For example, we can add a handler for the invalid opcode exception as follows:
```rust
// in src/interrupts/mod.rs
lazy_static! {
static ref IDT: idt::Idt = {
let mut idt = idt::Idt::new();
idt.set_handler(0, handler!(divide_by_zero_handler));
idt.set_handler(6, handler!(invalid_opcode_handler)); // new
idt
};
}
extern "C" fn invalid_opcode_handler(stack_frame: &ExceptionStackFrame)
-> !
{
let stack_frame = unsafe { &*stack_frame };
println!("\nEXCEPTION: INVALID OPCODE at {:#x}\n{:#?}",
stack_frame.instruction_pointer, stack_frame);
loop {}
}
```
Invalid opcode faults have the vector number 6, so we set the 6th IDT entry. This time we additionally print the address of the invalid instruction.
We can test our new handler with the special [ud2] instruction, which generates a invalid opcode:
[ud2]: https://www.felixcloutier.com/x86/ud
```rust
// in src/lib.rs
#[no_mangle]
pub extern "C" fn rust_main(multiboot_information_address: usize) {
...
// initialize our IDT
interrupts::init();
// provoke a invalid opcode exception
unsafe { asm!("ud2") };
println!("It did not crash!");
loop {}
}
```
## Exceptions with Error Codes
When a divide-by-zero exception occurs, we immediately know the reason: Someone tried to divide by zero. In contrast, there are faults with many possible causes. For example, a page fault occurs in many occasions: When accessing a non-present page, when writing to a read-only page, when the page table is malformed, etc. In order to differentiate these causes, the CPU pushes an additional error code onto the stack for such exceptions, which gives additional information.
### A new Macro
Since the CPU pushes an additional error code, the stack frame is different and our `handler!` macro is not applicable. Therefore we create a new `handler_with_error_code!` macro for them:
```rust
// in src/interrupts/mod.rs
macro_rules! handler_with_error_code {
($name: ident) => {{
#[naked]
extern "C" fn wrapper() -> ! {
unsafe {
asm!("pop rsi // pop error code into rsi
mov rdi, rsp
sub rsp, 8 // align the stack pointer
call $0"
:: "i"($name as extern "C" fn(
&ExceptionStackFrame, u64) -> !)
: "rdi","rsi" : "intel");
::core::intrinsics::unreachable();
}
}
wrapper
}}
}
```
The difference to the `handler!` macro is the additional error code argument. The CPU pushes the error code last, so we pop it right at the beginning of the wrapper function. We pop it into `rsi` because the C calling convention expects the second argument in it.
### A Page Fault Handler
Let's write a page fault handler which analyzes and prints the error code:
```rust
// in src/interrupts/mod.rs
extern "C" fn page_fault_handler(stack_frame: &ExceptionStackFrame,
error_code: u64) -> !
{
println!(
"\nEXCEPTION: PAGE FAULT with error code {:?}\n{:#?}",
error_code, unsafe { &*stack_frame });
loop {}
}
```
We need to register our new handler function in the static interrupt descriptor table (IDT):
```rust
// in src/interrupts/mod.rs
lazy_static! {
static ref IDT: idt::Idt = {
let mut idt = idt::Idt::new();
idt.set_handler(0, handler!(divide_by_zero_handler));
idt.set_handler(6, handler!(invalid_opcode_handler));
// new
idt.set_handler(14, handler_with_error_code!(page_fault_handler));
idt
};
}
```
Page faults have the vector number 14, so we set the 14th IDT entry.
#### Testing it
Let's test our new page fault handler by provoking a page fault in our main function:
```rust
// in src/lib.rs
#[no_mangle]
pub extern "C" fn rust_main(multiboot_information_address: usize) {
...
// initialize our IDT
interrupts::init();
// provoke a page fault
unsafe { *(0xdeadbeaf as *mut u64) = 42 };
println!("It did not crash!");
loop {}
}
```
We get the following output:
![QEMU: page fault with error code 2 and stack frame dump](qemu-page-fault-handler.png)
### The Page Fault Error Code
“Error code 2” is not really an useful error message. Let's improve this by creating a `PageFaultErrorCode` type:
```rust
// in src/interrupts/mod.rs
bitflags! {
struct PageFaultErrorCode: u64 {
const PROTECTION_VIOLATION = 1 << 0;
const CAUSED_BY_WRITE = 1 << 1;
const USER_MODE = 1 << 2;
const MALFORMED_TABLE = 1 << 3;
const INSTRUCTION_FETCH = 1 << 4;
}
}
```
- When the `PROTECTION_VIOLATION` flag is set, the page fault was caused e.g. by a write to a read-only page. If it's not set, it was caused by accessing a non-present page.
- The `CAUSED_BY_WRITE` flag specifies if the fault was caused by a write (if set) or a read (if not set).
- The `USER_MODE` flag is set when the fault occurred in non-privileged mode.
- The `MALFORMED_TABLE` flag is set when the page table entry has a 1 in a reserved field.
- When the `INSTRUCTION_FETCH` flag is set, the page fault occurred while fetching the next instruction.
Now we can improve our page fault error message by using the new `PageFaultErrorCode`. We also print the accessed memory address:
```rust
extern "C" fn page_fault_handler(stack_frame: &ExceptionStackFrame,
error_code: u64) -> !
{
use x86_64::registers::control_regs;
println!(
"\nEXCEPTION: PAGE FAULT while accessing {:#x}\
\nerror code: {:?}\n{:#?}",
unsafe { control_regs::cr2() },
PageFaultErrorCode::from_bits(error_code).unwrap(),
unsafe { &*stack_frame });
loop {}
}
```
The `from_bits` function tries to convert the `u64` into a `PageFaultErrorCode`. We use `unwrap` to panic if the error code has invalid bits set, since this indicates an error in our `PageFaultErrorCode` definition or a stack corruption. We also print the contents of the `cr2` register. It contains the accessed memory address, which was the cause of the page fault.
Now we get a useful error message when a page fault occurs, which allows us to debug it more easily:
![QEMU: output is now `PAGE FAULT with error code CAUSED_BY_WRITE`](qemu-page-fault-error-code.png)
As expected, the page fault was caused by write to `0xdeadbeaf`. The `PROTECTION_VIOLATION` flag is not set, so the accessed page was not present.
## What's next?
Now we're able to catch and analyze various exceptions. The next step is to _resolve_ exceptions, if possible. An example is [demand paging]: The OS swaps out memory pages to disk so that a page fault occurs when the page is accessed the next time. In that case, the OS can resolve the exception by bringing the page back into memory. Afterwards, the OS resumes the interrupted program as if nothing had happened.
[demand paging]: https://en.wikipedia.org/wiki/Demand_paging
The next post will implement the first portion of demand paging: saving and restoring the complete state of an program. This will allow us to transparently interrupt and resume programs in the future.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.3 KiB

View File

@@ -1,905 +0,0 @@
+++
title = "Returning from Exceptions"
weight = 3
path = "returning-from-exceptions"
aliases = ["returning-from-exceptions.html"]
date = 2016-09-21
template = "edition-1/page.html"
[extra]
updated = "2016-11-01"
+++
In this post, we learn how to return from exceptions correctly. In the course of this, we will explore the `iretq` instruction, the C calling convention, multimedia registers, and the red zone.
<!-- more -->
As always, the complete source code is on [GitHub]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a [gitter chat] and a comment section at the end of this page.
[GitHub]: https://github.com/phil-opp/blog_os/tree/returning_from_exceptions
[issues]: https://github.com/phil-opp/blog_os/issues
[gitter chat]: https://gitter.im/phil-opp/blog_os
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
[“Handling Exceptions”]: @/edition-1/posts/09-handling-exceptions/index.md
## Introduction
Most exceptions are fatal and can't be resolved. For example, we can't return from a divide-by-zero exception in a reasonable way. However, there are some exceptions that we can resolve:
Imagine a system that uses [memory mapped files]: We map a file into the virtual address space without loading it into memory. Whenever we access a part of the file for the first time, a page fault occurs. However, this page fault is not fatal. We can resolve it by loading the corresponding page from disk into memory and setting the `present` flag in the page table. Then we can return from the page fault handler and restart the failed instruction, which now successfully accesses the file data.
[memory mapped files]: https://en.wikipedia.org/wiki/Memory-mapped_file
Memory mapped files are completely out of scope for us right now (we have neither a file concept nor a hard disk driver). So we need an exception that we can resolve easily so that we can return from it in a reasonable way. Fortunately, there is an exception that needs no resolution at all: the breakpoint exception.
## The Breakpoint Exception
The breakpoint exception is the perfect exception to test our upcoming return-from-exception logic. Its only purpose is to temporary pause a program when the breakpoint instruction `int3` is executed.
The breakpoint exception is commonly used in debuggers: When the user sets a breakpoint, the debugger overwrites the corresponding instruction with the `int3` instruction so that the CPU throws the breakpoint exception when it reaches that line. When the user wants to continue the program, the debugger replaces the `int3` instruction with the original instruction again and continues the program. For more details, see the [How debuggers work] series.
[How debuggers work]: https://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints
For our use case, we don't need to overwrite any instructions (it wouldn't even be possible since we [set the page table flags] to read-only). Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program.
[set the page table flags]: @/edition-1/posts/07-remap-the-kernel/index.md#using-the-correct-flags
### Catching Breakpoints
Let's start by defining a handler function for the breakpoint exception:
```rust
// in src/interrupts/mod.rs
extern "C" fn breakpoint_handler(stack_frame: &ExceptionStackFrame) -> !
{
let stack_frame = unsafe { &*stack_frame };
println!("\nEXCEPTION: BREAKPOINT at {:#x}\n{:#?}",
stack_frame.instruction_pointer, stack_frame);
loop {}
}
```
We print an error message and also output the instruction pointer and the rest of the stack frame. Note that this function does _not_ return yet, since our `handler!` macro still requires a diverging function.
We need to register our new handler function in the interrupt descriptor table (IDT):
```rust
// in src/interrupts/mod.rs
lazy_static! {
static ref IDT: idt::Idt = {
let mut idt = idt::Idt::new();
idt.set_handler(0, handler!(divide_by_zero_handler));
idt.set_handler(3, handler!(breakpoint_handler)); // new
idt.set_handler(6, handler!(invalid_opcode_handler));
idt.set_handler(14, handler_with_error_code!(page_fault_handler));
idt
};
}
```
We set the IDT entry with number 3 since it's the vector number of the breakpoint exception.
#### Testing it
In order to test it, we insert an `int3` instruction in our `rust_main`:
```rust
// in src/lib.rs
...
#[macro_use] // needed for the `int!` macro
extern crate x86_64;
...
#[no_mangle]
pub extern "C" fn rust_main(...) {
...
interrupts::init();
// trigger a breakpoint exception
unsafe { int!(3) };
println!("It did not crash!");
loop {}
}
```
When we execute `make run`, we see the following:
![QEMU showing `EXCEPTION: BREAKPOINT at 0x110970` and a dump of the exception stack frame](qemu-breakpoint-handler.png)
It works! Now we “just” need to return from the breakpoint handler somehow so that we see the `It did not crash` message again.
## Returning from Exceptions
So how do we return from exceptions? To make it easier, we look at a normal function return first:
![function stack frame](function-stack-frame.svg)
When calling a function, the `call` instruction pushes the return address on the stack. When the called function is finished, it can return to the parent function through the `ret` instruction, which pops the return address from the stack and then jumps to it.
The exception stack frame, in contrast, looks a bit different:
![exception stack frame](exception-stack-frame.svg)
Instead of pushing a return address, the CPU pushes the stack and instruction pointers (with their segment descriptors), the RFLAGS register, and an optional error code. It also aligns the stack pointer to a 16 byte boundary before pushing values.
So we can't use a normal `ret` instruction, since it expects a different stack frame layout. Instead, there is a special instruction for returning from exceptions: `iretq`.
### The `iretq` Instruction
The `iretq` instruction is the one and only way to return from exceptions and is specifically designed for this purpose. The AMD64 instruction manual ([PDF][amd-manual]) even demands that `iretq` “_must_ be used to terminate the exception or interrupt handler associated with the exception”.
[amd-manual]: https://www.amd.com/system/files/TechDocs/24594.pdf
IRETQ restores `rip`, `cs`, `rflags`, `rsp`, and `ss` from the values saved on the stack and thus continues the interrupted program. The instruction does not handle the optional error code, so it must be popped from the stack before.
We see that `iretq` treats the stored instruction pointer as return address. For most exceptions, the stored `rip` points to the instruction that caused the fault. So by executing `iretq`, we restart the failing instruction. This makes sense because we should have resolved the exception when returning from it, so the instruction should no longer fail (e.g. the accessed part of the memory mapped file is now present in memory).
The situation is a bit different for the breakpoint exception, since it needs no resolution. Restarting the `int3` instruction wouldn't make sense, since it would cause a new breakpoint exception and we would enter an endless loop. For this reason the hardware designers decided that the stored `rip` should point to the next instruction after the `int3` instruction.
Let's check this for our breakpoint handler. Remember, the handler printed the following message (see the image above):
```
EXCEPTION: BREAKPOINT at 0x110970
```
So let's disassemble the instruction at `0x110970` and its predecessor:
```bash
> objdump -d build/kernel-x86_64.bin | grep -B1 "110970:"
11096f: cc int3
110970: 48 c7 01 2a 00 00 00 movq $0x2a,(%rcx)
```
We see that `0x110970` indeed points to the next instruction after `int3`. So we can simply jump to the stored instruction pointer when we want to return from the breakpoint exception.
### Implementation
Let's update our `handler!` macro to support non-diverging exception handlers:
```rust
// in src/interrupts/mod.rs
macro_rules! handler {
($name: ident) => {{
#[naked]
extern "C" fn wrapper() -> ! {
unsafe {
asm!("mov rdi, rsp
sub rsp, 8 // align the stack pointer
call $0"
:: "i"($name as extern "C" fn(
&ExceptionStackFrame)) // no longer diverging
: "rdi" : "intel", "volatile");
// new
asm!("add rsp, 8 // undo stack pointer alignment
iretq"
:::: "intel", "volatile");
::core::intrinsics::unreachable();
}
}
wrapper
}}
}
```
When an exception handler returns from the `call` instruction, we use the `iretq` instruction to continue the interrupted program. Note that we need to undo the stack pointer alignment before, so that `rsp` points to the end of the exception stack frame again.
We've changed the handler function type, so we need to adjust our existing exception handlers:
```diff
// in src/interrupts/mod.rs
extern "C" fn divide_by_zero_handler(
- stack_frame: &ExceptionStackFrame) -> ! {...}
+ stack_frame: &ExceptionStackFrame) {...}
extern "C" fn invalid_opcode_handler(
- stack_frame: &ExceptionStackFrame) -> ! {...}
+ stack_frame: &ExceptionStackFrame) {...}
extern "C" fn breakpoint_handler(
- stack_frame: &ExceptionStackFrame) -> ! {
+ stack_frame: &ExceptionStackFrame) {
println!(...);
- loop {}
}
```
Note that we also removed the `loop {}` at the end of our `breakpoint_handler` so that it no longer diverges. The `divide_by_zero_handler` and the `invalid_opcode_handler` still diverge (albeit the new function type would allow a return).
### Testing
Let's try our new `iretq` logic:
![QEMU output with `EXCEPTION BREAKPOINT` and `EXCEPTION PAGE FAULT` but no `It did not crash`](qemu-breakpoint-return-page-fault.png)
Instead of the expected _“It did not crash”_ message after the breakpoint exception, we get a page fault. The strange thing is that our kernel tried to access address `0x1`, which should never happen. So it seems like we messed up something important.
### Debugging
Let's debug it using GDB. For that we execute `make debug` in one terminal (which starts QEMU with the `-s -S` flags) and then `make gdb` (which starts and connects GDB) in a second terminal. For more information about GDB debugging, check out our [Set Up GDB] guide.
[Set Up GDB]: @/edition-1/extra/set-up-gdb/index.md
First we want to check if our `iretq` was successful. Therefore we set a breakpoint on the `println!("It did not crash line!")` statement in `src/lib.rs`. Let's assume that it's on line 61:
```
(gdb) break blog_os/src/lib.rs:61
Breakpoint 1 at 0x110a95: file /home/.../blog_os/src/lib.rs, line 61.
```
This line is after the `int3` instruction, so we know that the `iretq` succeeded when the breakpoint is hit. To test this, we continue the execution:
```
(gdb) continue
Continuing.
Breakpoint 1, blog_os::rust_main (multiboot_information_address=1539136)
at /home/.../blog_os/src/lib.rs:61
61 println!("It did not crash!");
```
It worked! So our kernel successfully returned from the `int3` instruction, which means that the `iretq` itself works.
However, when we `continue` the execution again, we get the page fault. So the exception occurs somewhere in the `println` logic. This means that it occurs in code generated by the compiler (and not e.g. in inline assembly). But the compiler should never access `0x1`, so how is this happening?
The answer is that we've used the wrong _calling convention_ for our exception handlers. Thus, we violate some compiler invariants so that the code that works fine without intermediate exceptions starts to violate memory safety when it's executed after a breakpoint exception.
## Calling Conventions
Exceptions are quite similar to function calls: The CPU jumps to the first instruction of the (handler) function and executes the function. Afterwards, if the function is not diverging, the CPU jumps to the return address and continues the execution of the parent function.
However, there is a major difference between exceptions and function calls: A function call is invoked voluntary by a compiler inserted `call` instruction, while an exception might occur at _any_ instruction. In order to understand the consequences of this difference, we need to examine function calls in more detail.
[Calling conventions] specify the details of a function call. For example, they specify where function parameters are placed (e.g. in registers or on the stack) and how results are returned. On x86_64 Linux, the following rules apply for C functions (specified in the [System V ABI]):
[Calling conventions]: https://en.wikipedia.org/wiki/Calling_convention
[System V ABI]: https://refspecs.linuxbase.org/elf/gabi41.pdf
- the first six integer arguments are passed in registers `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
- additional arguments are passed on the stack
- results are returned in `rax` and `rdx`
Note that Rust does not follow the C ABI (in fact, [there isn't even a Rust ABI yet][rust abi]). So these rules apply only to functions declared as `extern "C" fn`.
[rust abi]: https://github.com/rust-lang/rfcs/issues/600
### Preserved and Scratch Registers
The calling convention divides the registers in two parts: _preserved_ and _scratch_ registers.
The values of the preserved register must remain unchanged across function calls. So a called function (the _“callee”_) is only allowed to overwrite these registers if it restores their original values before returning. Therefore these registers are called _“callee-saved”_. A common pattern is to save these registers to the stack at the function's beginning and restore them just before returning.
In contrast, a called function is allowed to overwrite scratch registers without restrictions. If the caller wants to preserve the value of a scratch register across a function call, it needs to backup and restore it (e.g. by pushing it to the stack before the function call). So the scratch registers are _caller-saved_.
On x86_64, the C calling convention specifies the following preserved and scratch registers:
preserved registers | scratch registers
---|---
`rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11`
_callee-saved_ | _caller-saved_
The compiler knows these rules, so it generates the code accordingly. For example, most functions begin with a `push rbp`, which backups `rbp` on the stack (because it's a callee-saved register).
### The Exception Calling Convention
In contrast to function calls, exceptions can occur on _any_ instruction. In most cases we don't even know at compile time if the generated code will cause an exception. For example, the compiler can't know if an instruction causes a stack overflow or an other page fault.
Since we don't know when an exception occurs, we can't backup any registers before. This means that we can't use a calling convention that relies on caller-saved registers for our exception handlers. But we do so at the moment: Our exception handlers are declared as `extern "C" fn` and thus use the C calling convention.
So here is what happens:
- `rust_main` is executing; it writes some memory address into `rax`.
- The `int3` instruction causes a breakpoint exception.
- Our `breakpoint_handler` prints to the screen and assumes that it can overwrite `rax` freely (since it's a scratch register). Somehow the value `0` ends up in `rax`.
- We return from the breakpoint exception using `iretq`.
- `rust_main` continues and accesses the memory address in `rax`.
- The CPU tries to access address `0x1`, which causes a page fault.
So our exception handler erroneously assumes that the scratch registers were saved by the caller. But the caller (`rust_main`) couldn't save any registers since it didn't know that an exception occurs. So nobody saves `rax` and the other scratch registers, which leads to the page fault.
The problem is that we use a calling convention with caller-saved registers for our exception handlers. Instead, we need a calling convention means that preserves _all registers_. In other words, all registers must be callee-saved:
```rust
extern "all-registers-callee-saved" fn exception_handler() {...}
```
Unfortunately, Rust does not support such a calling convention. It was [proposed once][interrupt calling conventions], but did not get accepted for various reasons. The primary reason was that such calling conventions can be simulated by writing a naked wrapper function.
(Remember: [Naked functions] are functions without prologue and can contain only inline assembly. They were discussed in the [previous post][naked fn post].)
[interrupt calling conventions]: https://github.com/rust-lang/rfcs/pull/1275
[Naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
[naked fn post]: @/edition-1/extra/naked-exceptions/02-better-exception-messages/index.md#naked-functions
### A naked wrapper function
Such a naked wrapper function might look like this:
```rust
#[naked]
extern "C" fn calling_convention_wrapper() {
unsafe {
asm!("
push rax
push rcx
push rdx
push rsi
push rdi
push r8
push r9
push r10
push r11
// TODO: call exception handler with C calling convention
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rdx
pop rcx
pop rax
" :::: "intel", "volatile");
}
}
```
This wrapper function saves all _scratch_ registers to the stack before calling the exception handler and restores them afterwards. Note that we `pop` the registers in reverse order.
We don't need to backup _preserved_ registers since they are callee-saved in the C calling convention. Thus, the compiler already takes care of preserving their values.
### Fixing our Handler Macro
Let's update our handler macro to fix the calling convention problem. Therefore we need to backup and restore all scratch registers. For that we create two new macros:
```rust
// in src/interrupts/mod.rs
macro_rules! save_scratch_registers {
() => {
asm!("push rax
push rcx
push rdx
push rsi
push rdi
push r8
push r9
push r10
push r11
" :::: "intel", "volatile");
}
}
macro_rules! restore_scratch_registers {
() => {
asm!("pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rdx
pop rcx
pop rax
" :::: "intel", "volatile");
}
}
```
We need to declare these macros _above_ our `handler` macro, since macros are only available after their declaration.
Now we can use these macros to fix our `handler!` macro:
```rust
// in src/interrupts/mod.rs
macro_rules! handler {
($name: ident) => {{
#[naked]
extern "C" fn wrapper() -> ! {
unsafe {
save_scratch_registers!();
asm!("mov rdi, rsp
add rdi, 9*8 // calculate exception stack frame pointer
// sub rsp, 8 (stack is aligned already)
call $0"
:: "i"($name as
extern "C" fn(&ExceptionStackFrame))
: "rdi" : "intel", "volatile");
restore_scratch_registers!();
asm!("
// add rsp, 8 (undo stack alignment; not needed anymore)
iretq"
:::: "intel", "volatile");
::core::intrinsics::unreachable();
}
}
wrapper
}}
}
```
It's important that we save the registers first, before we modify any of them. After the `call` instruction (but before `iretq`) we restore the registers again. Because we're now changing `rsp` (by pushing the register values) before we load it into `rdi`, we would get a wrong exception stack frame pointer. Therefore we need to adjust it by adding the number of bytes we push. We push 9 registers that are 8 bytes each, so `9 * 8` bytes in total.
Note that we no longer need to manually align the stack pointer, because we're pushing an uneven number of registers in `save_scratch_registers`. Thus the stack pointer already has the required 16-byte alignment.
### Testing it again
Let's test it again with our corrected `handler!` macro:
![QEMU output with `EXCEPTION BREAKPOINT` and `It did not crash`](qemu-breakpoint-return.png)
The page fault is gone and we see the _“It did not crash”_ message again!
So the page fault occurred because our exception handler didn't preserve the scratch register `rax`. Our new `handler!` macro fixes this problem by saving all scratch registers (including `rax`) before calling exception handlers. Thus, `rax` still contains the valid memory address when `rust-main` continues execution.
## Multimedia Registers
When we discussed calling conventions above, we assumed that a x86_64 CPU only has the following 16 registers: `rax`, `rbx`, `rcx`, `rdx`, `rsi`, `rdi`, `rsp`, `rbp`, `r8`, `r9`, `r10`, `r11`.`r12`, `r13`, `r14`, and `r15`. These registers are called _general purpose registers_ since each of them can be used for arithmetic and load/store instructions.
However, modern CPUs also have a set of _special purpose registers_, which can be used to improve performance in several use cases. On x86_64, the most important set of special purpose registers are the _multimedia registers_. These registers are larger than the general purpose registers and can be used to speed up audio/video processing or matrix calculations. For example, we could use them to add two 4-dimensional vectors _in a single CPU instruction_:
![`(1,2,3,4) + (5,6,7,8) = (6,8,10,12)`](vector-addition.png)
Such multimedia instructions are called [Single Instruction Multiple Data (SIMD)] instructions, because they simultaneously perform an operation (e.g. addition) on multiple data words. Good compilers are able to transform normal loops into such SIMD code automatically. This process is called [auto-vectorization] and can lead to huge performance improvements.
[Single Instruction Multiple Data (SIMD)]: https://en.wikipedia.org/wiki/SIMD
[auto-vectorization]: https://en.wikipedia.org/wiki/Automatic_vectorization
However, auto-vectorization causes a problem for us: Most of the multimedia registers are caller-saved. According to our discussion of calling conventions above, this means that our exception handlers erroneously assume that they are allowed to overwrite them without preserving their values.
We don't use any multimedia registers explicitly, but the Rust compiler might auto-vectorize our code (including the exception handlers). Thus we could silently clobber the multimedia registers, which leads to the same problems as above:
![example: program uses mm0, mm1, and mm2. Then the exception handler clobbers mm1.](xmm-overwrite.svg)
This example shows a program that is using the first three multimedia registers (`mm0` to `mm2`). At some point, an exception occurs and control is transferred to the exception handler. The exception handler uses `mm1` for its own data and thus overwrites the previous value. When the exception is resolved, the CPU continues the interrupted program again. However, the program is now corrupt since it relies on the original `mm1` value.
### Saving and Restoring Multimedia Registers
In order to fix this problem, we need to backup all caller-saved multimedia registers before we call the exception handler. The problem is that the set of multimedia registers varies between CPUs. There are different standards:
- [MMX]: The MMX instruction set was introduced in 1997 and defines eight 64 bit registers called `mm0` through `mm7`. These registers are just aliases for the registers of the [x87 floating point unit].
- [SSE]: The _Streaming SIMD Extensions_ instruction set was introduced in 1999. Instead of re-using the floating point registers, it adds a completely new register set. The sixteen new registers are called `xmm0` through `xmm15` and are 128 bits each.
- [AVX]: The _Advanced Vector Extensions_ are extensions that further increase the size of the multimedia registers. The new registers are called `ymm0` through `ymm15` and are 256 bits each. They extend the `xmm` registers, so e.g. `xmm0` is the lower (or upper?) half of `ymm0`.
[MMX]: https://en.wikipedia.org/wiki/MMX_(instruction_set)
[x87 floating point unit]: https://en.wikipedia.org/wiki/X87
[SSE]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
[AVX]: https://en.wikipedia.org/wiki/Advanced_Vector_Extensions
The Rust compiler (and LLVM) assume that the `x86_64-unknown-linux-gnu` target supports only MMX and SSE, so we don't need to save the `ymm0` through `ymm15`. But we need to save `xmm0` through `xmm15` and also `mm0` through `mm7`. There is a special instruction to do this: [fxsave]. This instruction saves the floating point and multimedia state to a given address. It needs _512 bytes_ to store that state.
[fxsave]: https://www.felixcloutier.com/x86/fxsave
In order to save/restore the multimedia registers, we _could_ add new macros:
```rust
macro_rules! save_multimedia_registers {
() => {
asm!("sub rsp, 512
fxsave [rsp]
" :::: "intel", "volatile");
}
}
macro_rules! restore_multimedia_registers {
() => {
asm!("fxrstor [rsp]
add rsp, 512
" :::: "intel", "volatile");
}
}
```
First, we reserve the 512 bytes on the stack and then we use `fxsave` to backup the multimedia registers. In order to restore them later, we use the [fxrstor] instruction. Note that `fxsave` and `fxrstor` require a 16 byte aligned memory address.
[fxrstor]: https://www.felixcloutier.com/x86/fxrstor
However, _we won't do it that way_. The problem is the large amount of memory required. We will reuse the same code when we handle hardware interrupts in a future post. So for each mouse click, pressed key, or arrived network package we need to write 512 bytes to memory. This would be a huge performance problem.
Fortunately, there exists an alternative solution.
### Disabling Multimedia Extensions
We just disable MMX, SSE, and all the other fancy multimedia extensions in our kernel[^fn-userspace-sse]. This way, our exception handlers won't clobber the multimedia registers because they won't use them at all.
[^fn-userspace-sse]: Userspace programs will still be able to use the multimedia registers.
This solution has its own disadvantages, of course. For example, it leads to slower kernel code because the compiler can't perform any auto-vectorization optimizations. But it's still the faster solution (since we save many memory accesses) and most kernels do it this way (including Linux).
So how do we disable MMX and SSE? Well, we just tell the compiler that our target system doesn't support it. Since the very beginning, we're compiling our kernel for the `x86_64-unknown-linux-gnu` target. This worked fine so far, but now we want a different target without support for multimedia extensions. We can do so by creating a _target configuration file_.
### Target Specifications
In order to disable the multimedia extensions for our kernel, we need to compile for a custom target. We want a target that is equal to `x86_64-unknown-linux-gnu`, but without MMX and SSE support. Rust allows us to specify such a target using a JSON configuration file.
A minimal target specification that describes the `x86_64-unknown-linux-gnu` target looks like this:
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"arch": "x86_64",
"os": "none"
}
```
The `llvm-target` field specifies the target triple that is passed to LLVM. We want to derive a 64-bit Linux target, so we choose `x86_64-unknown-linux-gnu`. The `data-layout` field is also passed to LLVM and specifies how data should be laid out in memory. It consists of various specifications separated by a `-` character. For example, the `e` means little endian and `S128` specifies that the stack should be 128 bits (= 16 byte) aligned. The format is described in detail in the [LLVM documentation][data layout] but there shouldn't be a reason to change this string.
The other fields are used for conditional compilation. This allows crate authors to use `cfg` variables to write special code for depending on the OS or the architecture. There isn't any up-to-date documentation about these fields but the [corresponding source code][target specification] is quite readable.
[data layout]: https://llvm.org/docs/LangRef.html#data-layout
[target specification]: https://github.com/rust-lang/rust/blob/c772948b687488a087356cb91432425662e034b9/src/librustc_back/target/mod.rs#L194-L214
#### Disabling MMX and SSE
In order to disable the multimedia extensions, we create a new target named `x86_64-blog_os`. To describe this target, we create a file named `x86_64-blog_os.json` in the project root with the following content:
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"arch": "x86_64",
"os": "none",
"features": "-mmx,-sse"
}
```
It's equal to `x86_64-unknown-linux-gnu` target but has one additional option: `"features": "-mmx,-sse"`. So we added two target _features_: `-mmx` and `-sse`. The minus prefix defines that our target does _not_ support this feature. So by specifying `-mmx` and `-sse`, we disable the default `mmx` and `sse` features.
In order to compile for the new target, we need to adjust our Makefile:
```diff
# in `Makefile`
arch ?= x86_64
-target ?= $(arch)-unknown-linux-gnu
+target ?= $(arch)-blog_os
...
```
The new target name (`x86_64-blog_os`) is the file name of the JSON configuration file without the `.json` extension.
### Cross compilation
Let's try if our kernel still works with the new target:
```
> make run
Compiling raw-cpuid v2.0.1
Compiling rlibc v0.1.5
Compiling x86 v0.7.1
Compiling spin v0.3.5
error[E0463]: can't find crate for `core`
error: aborting due to previous error
Build failed, waiting for other jobs to finish...
...
Makefile:52: recipe for target 'cargo' failed
make: *** [cargo] Error 101
```
It doesn't compile anymore. The error tells us that the Rust compiler no longer finds the core library.
The [core library] is implicitly linked to all `no_std` crates and contains things such as `Result`, `Option`, and iterators. We've used that library without problems since [the very beginning], so why is it no longer available?
[core library]: https://doc.rust-lang.org/nightly/core/index.html
[the very beginning]: @/edition-1/posts/03-set-up-rust/index.md
The problem is that the core library is distributed together with the Rust compiler as a _precompiled_ library. So it is only valid for the host triple, which is `x86_64-unknown-linux-gnu` in our case. If we want to compile code for other targets, we need to recompile `core` for these targets first.
#### Xargo
That's where [xargo] comes in. It is a wrapper for cargo that eases cross compilation. We can install it by executing:
[xargo]: https://github.com/japaric/xargo
```
cargo install xargo
```
Xargo depends on the rust source code, which we can install with `rustup component add rust-src`.
Xargo is “a drop-in replacement for cargo”, so every cargo command also works with `xargo`. You can do e.g. `xargo --help`, `xargo clean`, or `xargo doc`. However, the `build` command gains additional functionality: `xargo build` will automatically cross compile the `core` library when compiling for custom targets.
That's exactly what we want, so we change one letter in our Makefile:
```diff
# in `Makefile`
...
cargo:
- @cargo build --target $(target)
+ @xargo build --target $(target)
...
```
Now the build goes through `xargo`, which should fix the compilation error. Let's try it out:
```
> make run
Compiling core v0.0.0 (file:///home/…/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore)
LLVM ERROR: SSE register return with SSE disabled
error: Could not compile `core`.
```
Well, we get a different error now, so it seems like we're making progress :). It seems like there is a “SSE register return” although SSE is disabled. But what's an “SSE register return”?
### SSE Register Return
Remember when we discussed calling conventions above? The calling convention defines which registers are used for return values. Well, the [System V ABI] defines that `xmm0` should be used for returning floating point values. So somewhere in the `core` library a function returns a float and LLVM doesn't know what to do. The ABI says “use `xmm0`” but the target specification says “don't use `xmm` registers”.
In order to fix this problem, we need to change our float ABI. The idea is to avoid normal hardware-supported floats and use a pure software implementation instead. We can do so by enabling the `soft-float` feature for our target. For that, we edit `x86_64-blog_os.json`:
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
...
"features": "-mmx,-sse,+soft-float"
}
```
The plus prefix tells LLVM to enable the `soft-float` feature.
Let's try `make run` again:
```
> make run
Compiling core v0.0.0 (file:///…/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore)
Finished release [optimized] target(s) in 21.95 secs
Compiling spin v0.4.5
Compiling once v0.3.2
Compiling x86 v0.8.0
Compiling bitflags v0.9.1
Compiling raw-cpuid v2.0.1
Compiling rlibc v0.1.5
Compiling linked_list_allocator v0.2.3
Compiling volatile v0.1.0
Compiling bitflags v0.4.0
Compiling bit_field v0.5.0
Compiling spin v0.3.5
Compiling multiboot2 v0.1.0
Compiling lazy_static v0.2.2
Compiling hole_list_allocator v0.1.0 (file:///…/libs/hole_list_allocator)
Compiling blog_os v0.1.0 (file:///…)
error[E0463]: can't find crate for `alloc`
--> src/lib.rs:33:1
|
33 | extern crate alloc;
| ^^^^^^^^^^^^^^^^^^^ can't find crate
error: aborting due to previous error
```
We see that `xargo` now compiles the `core` crate in release mode. Then it starts the normal cargo build. Cargo then recompiles all dependencies, since it needs to generate different code for the new target.
However, the build still fails. The reason is that xargo only installs `core` by default, but we also need the `alloc` crate. We can enable it by creating a file named `Xargo.toml` with the following contents:
```toml
# Xargo.toml
[target.x86_64-blog_os.dependencies]
alloc = {}
```
Now xargo compiles `alloc`, too:
```
> make run
Compiling core v0.0.0 (file:///…/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore)
Compiling std_unicode v0.0.0 (file:///…/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd_unicode)
Compiling alloc v0.0.0 (file:///…/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/liballoc)
Finished release [optimized] target(s) in 28.84 secs
Compiling blog_os v0.1.0 (file:///…/Documents/blog_os/master)
warning: unused variable: `allocator` […]
warning: unused variable: `frame` […]
Finished debug [unoptimized + debuginfo] target(s) in 1.75 secs
```
It worked! Now we have a kernel that never touches the multimedia registers! We can verify this by executing:
```
> objdump -d build/kernel-x86_64.bin | grep "mm[0-9]"
```
If the command produces no output, our kernel uses neither MMX (`mm0` `mm7`) nor SSE (`xmm0` `xmm15`) registers.
So now our return-from-exception logic works without problems in _most_ cases. However, there is still a pitfall hidden in the C calling convention, which might cause hideous bugs in some rare cases.
## The Red Zone
The [red zone] is an optimization of the [System V ABI] that allows functions to temporary use the 128 bytes below its stack frame without adjusting the stack pointer:
[red zone]: https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
![stack frame with red zone](red-zone.svg)
The image shows the stack frame of a function with `n` local variables. On function entry, the stack pointer is adjusted to make room on the stack for the local variables.
The red zone is defined as the 128 bytes below the adjusted stack pointer. The function can use this area for temporary data that's not needed across function calls. Thus, the two instructions for adjusting the stack pointer can be avoided in some cases (e.g. in small leaf functions).
However, this optimization leads to huge problems with exceptions. Let's assume that an exception occurs while a function uses the red zone:
![red zone overwritten by exception handler](red-zone-overwrite.svg)
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. It might fail or cause another exception, but it could also lead to strange bugs that [take weeks to debug].
[take weeks to debug]: https://forum.osdev.org/viewtopic.php?t=21720
### Adjusting our Exception Handler?
The problem is that the [System V ABI] demands that the red zone _“shall not be modified by signal or interrupt handlers.”_ Our current exception handlers do not respect this. We could try to fix it by subtracting 128 from the stack pointer before pushing anything:
```nasm
sub rsp, 128
save_scratch_registers()
...
call ...
...
restore_scratch_registers()
add rsp, 128
iretq
```
_This will not work._ The problem is that the CPU pushes the exception stack frame before even calling our handler function. So the CPU itself will clobber the red zone and there is nothing we can do about that. So our only chance is to disable the red zone.
### Disabling the Red Zone
The red zone is a property of our target, so in order to disable it we edit our `x86_64-blog_os.json` a last time:
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
...
"features": "-mmx,-sse,+soft-float",
"disable-redzone": true
}
```
We add one additional option at the end: `"disable-redzone": true`. As you might guess, this option disables the red zone optimization.
Now we have a red zone free kernel!
## Exceptions with Error Codes
We're now able to correctly return from exceptions without error codes. However, we still can't return from exceptions that push an error code (e.g. page faults). Let's fix that by updating our `handler_with_error_code` macro:
```rust
// in src/interrupts/mod.rs
macro_rules! handler_with_error_code {
($name: ident) => {{
#[naked]
extern "C" fn wrapper() -> ! {
unsafe {
asm!("pop rsi // pop error code into rsi
mov rdi, rsp
sub rsp, 8 // align the stack pointer
call $0"
:: "i"($name as extern "C" fn(
&ExceptionStackFrame, u64))
: "rdi","rsi" : "intel");
asm!("iretq" :::: "intel", "volatile");
::core::intrinsics::unreachable();
}
}
wrapper
}}
}
```
First, we change the type of the handler function: no more `-> !`, so it no longer needs to diverge. We also add an `iretq` instruction at the end.
Now we can make our `page_fault_handler` non-diverging:
```diff
// in src/interrupts/mod.rs
extern "C" fn page_fault_handler(stack_frame: &ExceptionStackFrame,
- error_code: u64) -> ! { ... }
+ error_code: u64) { ... }
```
However, now we have the same problem as above: The handler function will overwrite the scratch registers and cause bugs when returning. Let's fix this by invoking `save_scratch_registers` at the beginning:
```rust
// in src/interrupts/mod.rs
macro_rules! handler_with_error_code {
($name: ident) => {{
#[naked]
extern "C" fn wrapper() -> ! {
unsafe {
save_scratch_registers!();
asm!("pop rsi // pop error code into rsi
mov rdi, rsp
add rdi, 10*8 // calculate exception stack frame pointer
sub rsp, 8 // align the stack pointer
call $0
add rsp, 8 // undo stack pointer alignment
" :: "i"($name as extern "C" fn(
&ExceptionStackFrame, u64))
: "rdi","rsi" : "intel");
restore_scratch_registers!();
asm!("iretq" :::: "intel", "volatile");
::core::intrinsics::unreachable();
}
}
wrapper
}}
}
```
Now we backup the scratch registers to the stack right at the beginning and restore them just before the `iretq`. Like in the `handler` macro, we now need to add `10*8` to `rdi` in order to get the correct exception stack frame pointer (`save_scratch_registers` pushes nine 8 byte registers, plus the error code). We also need to undo the stack pointer alignment after the `call` [^fn-stack-alignment].
[^fn-stack-alignment]: The stack alignment is actually wrong here, since we additionally pushed an uneven number of registers. However, the `pop rsi` is wrong too, since the error code is no longer at the top of the stack. When we fix that problem, the stack alignment becomes correct again. So I left it in to keep things simple.
Now we have one last bug: We `pop` the error code into `rsi`, but the error code is no longer at the top of the stack (since `save_scratch_registers` pushed 9 registers on top of it). So we need to do it differently:
```rust
// in src/interrupts/mod.rs
macro_rules! handler_with_error_code {
($name: ident) => {{
#[naked]
extern "C" fn wrapper() -> ! {
unsafe {
save_scratch_registers!();
asm!("mov rsi, [rsp + 9*8] // load error code into rsi
mov rdi, rsp
add rdi, 10*8 // calculate exception stack frame pointer
sub rsp, 8 // align the stack pointer
call $0
add rsp, 8 // undo stack pointer alignment
" :: "i"($name as extern "C" fn(
&ExceptionStackFrame, u64))
: "rdi","rsi" : "intel");
restore_scratch_registers!();
asm!("add rsp, 8 // pop error code
iretq" :::: "intel", "volatile");
::core::intrinsics::unreachable();
}
}
wrapper
}}
}
```
Instead of using `pop`, we're calculating the error code address manually (`save_scratch_registers` pushes nine 8 byte registers) and load it into `rsi` using a `mov`. So now the error code stays on the stack. But `iretq` doesn't handle the error code, so we need to pop it before invoking `iretq`.
Phew! That was a lot of fiddling with assembly. Let's test if it still works.
### Testing
First, we test if the exception stack frame pointer and the error code are still correct:
```rust
// in rust_main in src/lib.rs
...
unsafe { int!(3) };
// provoke a page fault
unsafe { *(0xdeadbeaf as *mut u64) = 42; }
println!("It did not crash!");
loop {}
```
This should cause the following error message:
```
EXCEPTION: PAGE FAULT while accessing 0xdeadbeaf
error code: CAUSED_BY_WRITE
ExceptionStackFrame {
instruction_pointer: 1114753,
code_segment: 8,
cpu_flags: 2097158,
stack_pointer: 1171104,
stack_segment: 16
}
```
The error code should still be `CAUSED_BY_WRITE` and the exception stack frame values should also be correct (e.g. `code_segment` should be 8 and `stack_segment` should be 16).
#### Returning from Page Faults
Let's see what happens if we comment out the trailing `loop` in our page fault handler:
![QEMU printing the same page fault message again and again](qemu-page-fault-return.png)
We see that the same error message is printed over and over again. Here is what happens:
- The CPU executes `rust_main` and tries to access `0xdeadbeaf`. This causes a page fault.
- The page fault handler prints an error message and returns without fixing the cause of the exception (`0xdeadbeaf` is still unaccessible).
- The CPU restarts the instruction that caused the page fault and thus tries to access `0xdeadbeaf` again. Of course, this causes a page fault again.
- The page fault handler prints the error message and returns.
… and so on. Thus, our code indefinitely jumps between the page fault handler and the instruction that accesses `0xdeadbeaf`.
This is a good thing! It means that our `iretq` logic is working correctly, since it returns to the correct instruction every time. So our `handler_with_error_code` macro seems to be correct.
## What's next?
We are now able to catch exceptions and to return from them. However, there are still exceptions that completely crash our kernel by causing a [triple fault]. In the next post, we will fix this issue by handling a special type of exception: the [double fault]. Thus, we will be able to avoid random reboots in our kernel.
[triple fault]: https://en.wikipedia.org/wiki/Triple_fault
[double fault]: https://en.wikipedia.org/wiki/Double_fault

View File

@@ -1,92 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
<svg width="17cm" height="11cm" viewBox="-60 -21 340 212" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<text font-size="9.03111" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="90" y="65.9389">
<tspan x="90" y="65.9389"></tspan>
</text>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="204" y1="70" x2="176.236" y2="70"/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="185.118,65 175.118,70 185.118,75 "/>
</g>
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="208" y="72.75">
<tspan x="208" y="72.75">Old Stack Pointer</tspan>
</text>
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="75" y="230">
<tspan x="75" y="230"></tspan>
</text>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="70" x2="-4" y2="70"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="190" x2="-4" y2="190"/>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="-12" y1="72.2361" x2="-12" y2="187.764"/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-7,81.118 -12,71.118 -17,81.118 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-17,178.882 -12,188.882 -7,178.882 "/>
</g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="0" y1="-20" x2="0" y2="0"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="170" y1="-20" x2="170" y2="0"/>
<g>
<rect style="fill: #00ff00" x="0" y="0" width="170" height="20"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="0" width="170" height="20"/>
</g>
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="13.125">
<tspan x="85" y="13.125">Return Address</tspan>
</text>
<text font-size="9.03097" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="84" y="4">
<tspan x="84" y="4"></tspan>
</text>
<g>
<rect style="fill: #dddddd" x="0" y="20" width="170" height="20"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="20" width="170" height="20"/>
</g>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="33.125">
<tspan x="85" y="33.125">Local Variable 1</tspan>
</text>
<g>
<rect style="fill: #cccccc" x="0" y="40" width="170" height="32"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x="0" y="40" width="170" height="32"/>
</g>
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="59.125">
<tspan x="85" y="59.125">Local Variables 2..n</tspan>
</text>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="40" x2="170" y2="40"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="72" x2="120" y2="72"/>
<g>
<rect style="fill: #ff3333" x="0" y="70" width="170" height="120"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="70" width="170" height="120"/>
</g>
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="40" y="179.125">
<tspan x="40" y="179.125">Red Zone</tspan>
</text>
<text font-size="7.9021" style="fill: #000000;text-anchor:end;font-family:sans-serif;font-style:normal;font-weight:normal" x="-20" y="132.75">
<tspan x="-20" y="132.75">128 bytes</tspan>
</text>
<g>
<rect style="fill: #ffc200" x="30" y="70" width="140" height="30"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="70" width="140" height="30"/>
</g>
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="88.125">
<tspan x="100" y="88.125">Exception Stack Frame</tspan>
</text>
<g>
<rect style="fill: #ffe000" x="30" y="100" width="140" height="30"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="100" width="140" height="30"/>
</g>
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="118.125">
<tspan x="100" y="118.125">Register Backup</tspan>
</text>
<g>
<rect style="fill: #c6db97" x="30" y="130" width="140" height="30"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="130" width="140" height="30"/>
</g>
<text font-size="8.46654" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="147.925">
<tspan x="100" y="147.925">Handler Function Stack Frame</tspan>
</text>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #ff3333" x1="30" y1="70" x2="30" y2="160"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #ff3333" x1="30" y1="160" x2="170" y2="160"/>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="204" y1="160" x2="176.236" y2="160"/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="185.118,155 175.118,160 185.118,165 "/>
</g>
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="208" y="162.635">
<tspan x="208" y="162.635">New Stack Pointer</tspan>
</text>
</svg>

Before

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
<svg width="14cm" height="9cm" viewBox="-60 -21 270 172" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<text font-size="9.03111" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="90" y="65.9389">
<tspan x="90" y="65.9389"></tspan>
</text>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="154" y1="70" x2="126.236" y2="70"/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="135.118,65 125.118,70 135.118,75 "/>
</g>
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="158" y="72.75">
<tspan x="158" y="72.75">Stack Pointer</tspan>
</text>
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="75" y="230">
<tspan x="75" y="230"></tspan>
</text>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="70" x2="-4" y2="70"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="150" x2="-4" y2="150"/>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="-12" y1="72.2361" x2="-12" y2="147.764"/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-7,81.118 -12,71.118 -17,81.118 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-17,138.882 -12,148.882 -7,138.882 "/>
</g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="0" y1="-20" x2="0" y2="0"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="120" y1="-20" x2="120" y2="0"/>
<g>
<rect style="fill: #00ff00" x="0" y="0" width="120" height="20"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="0" width="120" height="20"/>
</g>
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="13.125">
<tspan x="60" y="13.125">Return Address</tspan>
</text>
<text font-size="9.03097" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="84" y="4">
<tspan x="84" y="4"></tspan>
</text>
<g>
<rect style="fill: #dddddd" x="0" y="20" width="120" height="20"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="20" width="120" height="20"/>
</g>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="33.125">
<tspan x="60" y="33.125">Local Variable 1</tspan>
</text>
<g>
<rect style="fill: #cccccc" x="0" y="40" width="120" height="32"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x="0" y="40" width="120" height="32"/>
</g>
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="59.125">
<tspan x="60" y="59.125">Local Variables 2..n</tspan>
</text>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="40" x2="120" y2="40"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="72" x2="120" y2="72"/>
<g>
<rect style="fill: #ff3333" x="0" y="70" width="120" height="80"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="70" width="120" height="80"/>
</g>
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="113.125">
<tspan x="60" y="113.125">Red Zone</tspan>
</text>
<text font-size="7.9021" style="fill: #000000;text-anchor:end;font-family:sans-serif;font-style:normal;font-weight:normal" x="-20" y="112.75">
<tspan x="-20" y="112.75">128 bytes</tspan>
</text>
</svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -1,114 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
<svg width="13cm" height="4cm" viewBox="-1 -23 252 63" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<rect style="fill: #00ff00" x="0" y="0" width="50" height="10"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="0" width="50" height="10"/>
</g>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="25" y="8.125">
<tspan x="25" y="8.125">mm0</tspan>
</text>
<g>
<rect style="fill: #00ff00" x="0" y="10" width="50" height="10"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="10" width="50" height="10"/>
</g>
<g>
<rect style="fill: #00ff00" x="0" y="20" width="50" height="10"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="20" width="50" height="10"/>
</g>
<g>
<rect style="fill: #dddddd" x="0" y="30" width="50" height="10"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="30" width="50" height="10"/>
</g>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="25" y="17.9931">
<tspan x="25" y="17.9931">mm1</tspan>
</text>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="25" y="28.125">
<tspan x="25" y="28.125">mm2</tspan>
</text>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="25" y="38.125">
<tspan x="25" y="38.125">mm3</tspan>
</text>
<g>
<rect style="fill: #00ff00" x="100" y="-4.44089e-15" width="50" height="10"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="100" y="-4.44089e-15" width="50" height="10"/>
</g>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="125" y="8.125">
<tspan x="125" y="8.125">mm0</tspan>
</text>
<g>
<rect style="fill: #ff0000" x="100" y="10" width="50" height="10"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="100" y="10" width="50" height="10"/>
</g>
<g>
<rect style="fill: #00ff00" x="100" y="20" width="50" height="10"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="100" y="20" width="50" height="10"/>
</g>
<g>
<rect style="fill: #dddddd" x="100" y="30" width="50" height="10"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="100" y="30" width="50" height="10"/>
</g>
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="125" y="18.125">
<tspan x="125" y="18.125">mm1</tspan>
</text>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="125" y="28.125">
<tspan x="125" y="28.125">mm2</tspan>
</text>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="125" y="38.125">
<tspan x="125" y="38.125">mm3</tspan>
</text>
<g>
<rect style="fill: #00ff00" x="200" y="0" width="50" height="10"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="200" y="0" width="50" height="10"/>
</g>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="225" y="8.125">
<tspan x="225" y="8.125">mm0</tspan>
</text>
<g>
<rect style="fill: #ff0000" x="200" y="10" width="50" height="10"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="200" y="10" width="50" height="10"/>
</g>
<g>
<rect style="fill: #00ff00" x="200" y="20" width="50" height="10"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="200" y="20" width="50" height="10"/>
</g>
<g>
<rect style="fill: #dddddd" x="200" y="30" width="50" height="10"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="200" y="30" width="50" height="10"/>
</g>
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="225" y="18.125">
<tspan x="225" y="18.125">mm1</tspan>
</text>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="225" y="28.125">
<tspan x="225" y="28.125">mm2</tspan>
</text>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="225" y="38.125">
<tspan x="225" y="38.125">mm3</tspan>
</text>
<text font-size="7.9021" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="25" y="-10.0787">
<tspan x="25" y="-10.0787">Program</tspan>
</text>
<text font-size="7.9021" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="125" y="-15.0175">
<tspan x="125" y="-15.0175">Exception</tspan>
<tspan x="125" y="-5.13977">Handler</tspan>
</text>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="50" y1="-12.8287" x2="97.7639" y2="-12.8287"/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="94.882,-10.8287 98.882,-12.8287 94.882,-14.8287 "/>
</g>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="150" y1="-12.8287" x2="197.764" y2="-12.8287"/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="194.882,-10.8287 198.882,-12.8287 194.882,-14.8287 "/>
</g>
<text font-size="7.9021" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="225" y="-10.0787">
<tspan x="225" y="-10.0787">Program</tspan>
</text>
<text font-size="6.77323" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="75" y="-14.737">
<tspan x="75" y="-14.737">Exception</tspan>
<tspan x="75" y="-6.27032">occurs</tspan>
</text>
<text font-size="6.77323" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="175" y="-14.737">
<tspan x="175" y="-14.737">Exception</tspan>
<tspan x="175" y="-6.27032">resolved</tspan>
</text>
</svg>

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1,7 +0,0 @@
+++
title = "Handling Exceptions using naked Functions"
sort_by = "weight"
template = "edition-1/handling-exceptions-with-naked-fns.html"
insert_anchor_links = "left"
aliases = ["first-edition/extra/naked-exceptions/index.html"]
+++

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -1,78 +0,0 @@
+++
title = "Set Up GDB"
template = "plain.html"
path = "set-up-gdb"
aliases = ["set-up-gdb.html"]
weight = 4
+++
There are a lot of things that can go wrong when developing an OS. So it's a good idea to add a debugger to our toolset, which allows us to set breakpoints and examine variables. We will use [GDB](https://www.gnu.org/software/gdb/) as QEMU supports it out of the box.
### QEMU parameters
To make QEMU listen for a gdb connection, we add the `-s` flag to the `run` target in our Makefile:
```make
run: $(iso)
@qemu-system-x86_64 -cdrom $(iso) -s
```
This allows us to connect a debugger at any time, for example to investigate why a panic occurred.
To wait for a debugger connection on startup, we add a `debug` target to the Makefile:
```make
debug: $(iso)
@qemu-system-x86_64 -cdrom $(iso) -s -S
```
It is identical to the `run` target except for the additional `-S` flag. This flag causes QEMU to freeze on startup and wait until a debugger is connected. Now it _should_ be possible to connect gdb.
### The annoying issue
Unfortunately gdb has an issue with the switch to long mode. If we connect when the CPU is already in long mode, everything works fine. But if we use `make debug` and thus connect right at the start, we get an error when we set a breakpoint in 64-bit mode:
```
Remote 'g' packet reply is too long: [a very long number]
```
This issue is known [since 2012][gdb issue patch] but it is still not fixed. Maybe we find the reason in the [issue thread][gdb issue thread]:
[gdb issue patch]: https://web.archive.org/web/20190114181420/https://www.cygwin.com/ml/gdb-patches/2012-03/msg00116.html
[gdb issue thread]: https://sourceware.org/bugzilla/show_bug.cgi?id=13984#c11
> from my (limited) experience, unless you ping the gdb-patches list weekly, this patch is more likely to remain forgotten :-)
Pretty frustrating, especially since the patch is [very small][gdb patch commit].
[gdb patch commit]: https://github.com/phil-opp/binutils-gdb/commit/9e88c451844ad38bb82fe77d1f388c87c41b4520
### Building the patched GDB
So the only way to use gdb with `make debug` is to build a modified gdb version that includes the patch. I created a repository with the patched GDB to make this easy. Just follow [the build instructions].
[the build instructions]: https://github.com/phil-opp/binutils-gdb#gdb-for-64-bit-rust-operating-systems
### Connecting GDB
Now you should have a `rust-os-gdb` subfolder. In its `bin` directory you find the `gdb` executable and the `rust-gdb` script, which [improves rendering of Rust types]. To make it easy to use it for our OS, we add a `make gdb` target to our Makefile:
[improves rendering of Rust types]: https://michaelwoerister.github.io/2015/03/27/rust-xxdb.html
```make
gdb:
@rust-os-gdb/bin/rust-gdb "build/kernel-x86_64.bin" -ex "target remote :1234"
```
It loads the debug information from our kernel binary and connects to the `localhost:1234` port, on which QEMU listens by default.
### Using GDB
After connecting to QEMU, you can use various gdb commands to control execution and examine data. All commands can be abbreviated as long they are still unique. For example, you can write `c` or `cont` instead of `continue`. The most important commands are:
- `help` or `h`: Show the help.
- `break` or `b`: Set a breakpoint. It possible to break on functions such as `rust_main` or on source lines such as `lib.rs:42`. You can use tab for autocompletion and omit parts of the path as long it's still unique. To modify breakpoints, you can use `disable`, `enable`, and `delete` plus the breakpoint number.
- `continue` or `c`: Continue execution until a breakpoint is reached.
- `next` or `n`: Step over the current line and break on the next line of the function. Sometimes this doesn't work in Rust OSes.
- `step` or `s`: Step into the current line, i.e. jump to the called function. Sometimes this doesn't work in Rust OSes.
- `list` or `l`: Shows the source code around the current position.
- `print` or `p`: Prints the value of a variable. You can use Cs `*` and `&` operators. To print in hexadecimal, use `p/x`.
- `tui enable`: Enables the text user interface, which provides a graphical interface (see below). To disable it again, run `tui disable`.
![gdb text user interface](gdb-tui-screenshot.png)
Of course there are many more commands. Feel free to send a PR if you think this list is missing something important. For a more complete GDB overview, check out [Beej's Quick Guide][bggdb] or the [website for Harvard's CS161 course][CS161].
[bggdb]: https://beej.us/guide/bggdb/
[CS161]: https://www.eecs.harvard.edu/~cs161/resources/gdb.html

View File

@@ -1,14 +0,0 @@
+++
title = "Talks"
path = "talks"
template = "plain.html"
weight = 1
+++
## 2018
- “The Rust Way Of OS Development” at HTWG Konstanz, May 30, 2018: [slides](https://phil-opp.github.io/talk-konstanz-may-2018/) [pdf](https://phil-opp.github.io/talk-konstanz-may-2018/talk.pdf)
## 2017
- “Open Source OS Development in Rust” at HTWG Konstanz, May 22, 2017: [slides](https://phil-opp.github.io/talk-konstanz-may-2017/)

View File

@@ -1,324 +0,0 @@
+++
title = "A minimal Multiboot Kernel"
weight = 1
path = "multiboot-kernel"
aliases = ["multiboot-kernel.html", "/2015/08/18/multiboot-kernel/", "/rust-os/multiboot-kernel.html"]
date = 2015-08-18
template = "edition-1/page.html"
+++
This post explains how to create a minimal x86 operating system kernel using the Multiboot standard. In fact, it will just boot and print `OK` to the screen. In subsequent blog posts we will extend it using the [Rust] programming language.
[Rust]: https://www.rust-lang.org/
<!-- more -->
I tried to explain everything in detail and to keep the code as simple as possible. If you have any questions, suggestions or other issues, please leave a comment or [create an issue] on Github. The source code is available in a [repository][source code], too.
[create an issue]: https://github.com/phil-opp/blog_os/issues
[source code]: https://github.com/phil-opp/blog_os/tree/first_edition_post_1/src/arch/x86_64
Note that this tutorial is written mainly for Linux. For some known problems on OS X see the comment section and [this issue][mac os issue]. If you want to use a virtual Linux machine, you can find instructions and a Vagrantfile in Ashley Willams's [x86-kernel repository].
[mac os issue]: https://github.com/phil-opp/blog_os/issues/55
[x86-kernel repository]: https://github.com/ashleygwilliams/x86-kernel
## Overview
When you turn on a computer, it loads the [BIOS] from some special flash memory. The BIOS runs self test and initialization routines of the hardware, then it looks for bootable devices. If it finds one, the control is transferred to its _bootloader_, which is a small portion of executable code stored at the device's beginning. The bootloader has to determine the location of the kernel image on the device and load it into memory. It also needs to switch the CPU to the so-called [protected mode] because x86 CPUs start in the very limited [real mode] by default (to be compatible to programs from 1978).
[BIOS]: https://en.wikipedia.org/wiki/BIOS
[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
[real mode]: https://wiki.osdev.org/Real_Mode
We won't write a bootloader because that would be a complex project on its own (if you really want to do it, check out [_Rolling Your Own Bootloader_]). Instead we will use one of the [many well-tested bootloaders][bootloader comparison] out there to boot our kernel from a CD-ROM. But which one?
[_Rolling Your Own Bootloader_]: https://wiki.osdev.org/Rolling_Your_Own_Bootloader
[bootloader comparison]: https://en.wikipedia.org/wiki/Comparison_of_boot_loaders
## Multiboot
Fortunately there is a bootloader standard: the [Multiboot Specification][multiboot]. Our kernel just needs to indicate that it supports Multiboot and every Multiboot-compliant bootloader can boot it. We will use the Multiboot 2 specification ([PDF][Multiboot 2]) together with the well-known [GRUB 2] bootloader.
[multiboot]: https://en.wikipedia.org/wiki/Multiboot_Specification
[multiboot 2]: https://nongnu.askapache.com/grub/phcoder/multiboot.pdf
[grub 2]: https://wiki.osdev.org/GRUB_2
To indicate our Multiboot 2 support to the bootloader, our kernel must start with a _Multiboot Header_, which has the following format:
Field | Type | Value
------------- | --------------- | ----------------------------------------
magic number | u32 | `0xE85250D6`
architecture | u32 | `0` for i386, `4` for MIPS
header length | u32 | total header size, including tags
checksum | u32 | `-(magic + architecture + header_length)`
tags | variable |
end tag | (u16, u16, u32) | `(0, 0, 8)`
Converted to a x86 assembly file it looks like this (Intel syntax):
```nasm
section .multiboot_header
header_start:
dd 0xe85250d6 ; magic number (multiboot 2)
dd 0 ; architecture 0 (protected mode i386)
dd header_end - header_start ; header length
; checksum
dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start))
; insert optional multiboot tags here
; required end tag
dw 0 ; type
dw 0 ; flags
dd 8 ; size
header_end:
```
If you don't know x86 assembly, here is some quick guide:
- the header will be written to a section named `.multiboot_header` (we need this later)
- `header_start` and `header_end` are _labels_ that mark a memory location, we use them to calculate the header length easily
- `dd` stands for `define double` (32bit) and `dw` stands for `define word` (16bit). They just output the specified 32bit/16bit constant.
- the additional `0x100000000` in the checksum calculation is a small hack[^fn-checksum_hack] to avoid a compiler warning
We can already _assemble_ this file (which I called `multiboot_header.asm`) using `nasm`. It produces a flat binary by default, so the resulting file just contains our 24 bytes (in little endian if you work on a x86 machine):
```
> nasm multiboot_header.asm
> hexdump -x multiboot_header
0000000 50d6 e852 0000 0000 0018 0000 af12 17ad
0000010 0000 0000 0008 0000
0000018
```
## The Boot Code
To boot our kernel, we must add some code that the bootloader can call. Let's create a file named `boot.asm`:
```nasm
global start
section .text
bits 32
start:
; print `OK` to screen
mov dword [0xb8000], 0x2f4b2f4f
hlt
```
There are some new commands:
- `global` exports a label (makes it public). As `start` will be the entry point of our kernel, it needs to be public.
- the `.text` section is the default section for executable code
- `bits 32` specifies that the following lines are 32-bit instructions. It's needed because the CPU is still in [Protected mode] when GRUB starts our kernel. When we switch to [Long mode] in the [next post] we can use `bits 64` (64-bit instructions).
- the `mov dword` instruction moves the 32bit constant `0x2f4b2f4f` to the memory at address `b8000` (it prints `OK` to the screen, an explanation follows in the next posts)
- `hlt` is the halt instruction and causes the CPU to stop
Through assembling, viewing and disassembling we can see the CPU [Opcodes] in action:
[Opcodes]: https://en.wikipedia.org/wiki/Opcode
```
> nasm boot.asm
> hexdump -x boot
0000000 05c7 8000 000b 2f4b 2f4f 00f4
000000b
> ndisasm -b 32 boot
00000000 C70500800B004B2F mov dword [dword 0xb8000],0x2f4b2f4f
-4F2F
0000000A F4 hlt
```
## Building the Executable
To boot our executable later through GRUB, it should be an [ELF] executable. So we want `nasm` to create ELF [object files] instead of plain binaries. To do that, we simply pass the `f elf64` argument to it.
[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
[object files]: https://wiki.osdev.org/Object_Files
To create the ELF _executable_, we need to [link] the object files together. We use a custom [linker script] named `linker.ld`:
[link]: https://en.wikipedia.org/wiki/Linker_(computing)
[linker script]: https://sourceware.org/binutils/docs/ld/Scripts.html
```ld
ENTRY(start)
SECTIONS {
. = 1M;
.boot :
{
/* ensure that the multiboot header is at the beginning */
*(.multiboot_header)
}
.text :
{
*(.text)
}
}
```
Let's translate it:
- `start` is the entry point, the bootloader will jump to it after loading the kernel
- `. = 1M;` sets the load address of the first section to 1 MiB, which is a conventional place to load a kernel[^Linker 1M]
- the executable will have two sections: `.boot` at the beginning and `.text` afterwards
- the `.text` output section contains all input sections named `.text`
- Sections named `.multiboot_header` are added to the first output section (`.boot`) to ensure they are at the beginning of the executable. This is necessary because GRUB expects to find the Multiboot header very early in the file.
So let's create the ELF object files and link them using our new linker script:
```
> nasm -f elf64 multiboot_header.asm
> nasm -f elf64 boot.asm
> ld -n -o kernel.bin -T linker.ld multiboot_header.o boot.o
```
It's important to pass the `-n` (or `--nmagic`) flag to the linker, which disables the automatic section alignment in the executable. Otherwise the linker may page align the `.boot` section in the executable file. If that happens, GRUB isn't able to find the Multiboot header because it isn't at the beginning anymore.
We can use `objdump` to print the sections of the generated executable and verify that the `.boot` section has a low file offset:
```
> objdump -h kernel.bin
kernel.bin: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .boot 00000018 0000000000100000 0000000000100000 00000080 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .text 0000000b 0000000000100020 0000000000100020 000000a0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
```
_Note_: The `ld` and `objdump` commands are platform specific. If you're _not_ working on x86_64 architecture, you will need to [cross compile binutils]. Then use `x86_64elfld` and `x86_64elfobjdump` instead of `ld` and `objdump`.
[cross compile binutils]: @/edition-1/extra/cross-compile-binutils.md
## Creating the ISO
All PC BIOSes know how to boot from a CD-ROM, so we want to create a bootable CD-ROM image, containing our kernel and the GRUB bootloader's files, in a single file called an [ISO](https://en.wikipedia.org/wiki/ISO_image). Make the following directory structure and copy the `kernel.bin` to the right place:
```
isofiles
└── boot
├── grub
│ └── grub.cfg
└── kernel.bin
```
The `grub.cfg` specifies the file name of our kernel and its Multiboot 2 compliance. It looks like this:
```
set timeout=0
set default=0
menuentry "my os" {
multiboot2 /boot/kernel.bin
boot
}
```
Now we can create a bootable image using the command:
```
grub-mkrescue -o os.iso isofiles
```
_Note_: `grub-mkrescue` causes problems on some platforms. If it does not work for you, try the following steps:
- try to run it with `--verbose`
- make sure `xorriso` is installed (`xorriso` or `libisoburn` package)
- If you're using an EFI-system, `grub-mkrescue` tries to create an EFI image by default. You can either pass `-d /usr/lib/grub/i386-pc` to avoid EFI or install the `mtools` package to get a working EFI image
- on some system the command is named `grub2-mkrescue`
## Booting
Now it's time to boot our OS. We will use [QEMU]:
[QEMU]: https://en.wikipedia.org/wiki/QEMU
```
qemu-system-x86_64 -cdrom os.iso
```
![qemu output](qemu-ok.png)
Notice the green `OK` in the upper left corner. If it does not work for you, take a look at the comment section.
Let's summarize what happens:
1. the BIOS loads the bootloader (GRUB) from the virtual CD-ROM (the ISO)
2. the bootloader reads the kernel executable and finds the Multiboot header
3. it copies the `.boot` and `.text` sections to memory (to addresses `0x100000` and `0x100020`)
4. it jumps to the entry point (`0x100020`, you can obtain it through `objdump -f`)
5. our kernel prints the green `OK` and stops the CPU
You can test it on real hardware, too. Just burn the ISO to a disk or USB stick and boot from it.
## Build Automation
Right now we need to execute 4 commands in the right order every time we change a file. That's bad. So let's automate the build using a `Makefile`. But first we should create some clean directory structure for our source files to separate the architecture specific files:
```
├── Makefile
└── src
└── arch
└── x86_64
├── multiboot_header.asm
├── boot.asm
├── linker.ld
└── grub.cfg
```
The Makefile looks like this (indented with tabs instead of spaces):
```Makefile
arch ?= x86_64
kernel := build/kernel-$(arch).bin
iso := build/os-$(arch).iso
linker_script := src/arch/$(arch)/linker.ld
grub_cfg := src/arch/$(arch)/grub.cfg
assembly_source_files := $(wildcard src/arch/$(arch)/*.asm)
assembly_object_files := $(patsubst src/arch/$(arch)/%.asm, \
build/arch/$(arch)/%.o, $(assembly_source_files))
.PHONY: all clean run iso
all: $(kernel)
clean:
@rm -r build
run: $(iso)
@qemu-system-x86_64 -cdrom $(iso)
iso: $(iso)
$(iso): $(kernel) $(grub_cfg)
@mkdir -p build/isofiles/boot/grub
@cp $(kernel) build/isofiles/boot/kernel.bin
@cp $(grub_cfg) build/isofiles/boot/grub
@grub-mkrescue -o $(iso) build/isofiles 2> /dev/null
@rm -r build/isofiles
$(kernel): $(assembly_object_files) $(linker_script)
@ld -n -T $(linker_script) -o $(kernel) $(assembly_object_files)
# compile assembly files
build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm
@mkdir -p $(shell dirname $@)
@nasm -felf64 $< -o $@
```
Some comments (see the [Makefile tutorial] if you don't know `make`):
- the `$(wildcard src/arch/$(arch)/*.asm)` chooses all assembly files in the src/arch/$(arch)` directory, so you don't have to update the Makefile when you add a file
- the `patsubst` operation for `assembly_object_files` just translates `src/arch/$(arch)/XYZ.asm` to `build/arch/$(arch)/XYZ.o`
- the `$<` and `$@` in the assembly target are [automatic variables]
- if you're using [cross-compiled binutils][cross compile binutils] just replace `ld` with `x86_64elfld`
[automatic variables]: https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
Now we can invoke `make` and all updated assembly files are compiled and linked. The `make iso` command also creates the ISO image and `make run` will additionally start QEMU.
## What's next?
In the [next post] we will create a page table and do some CPU configuration to switch to the 64-bit [long mode].
[next post]: @/edition-1/posts/02-entering-longmode/index.md
[long mode]: https://en.wikipedia.org/wiki/Long_mode
## Footnotes
[^fn-checksum_hack]: The formula from the table, `-(magic + architecture + header_length)`, creates a negative value that doesn't fit into 32bit. By subtracting from `0x100000000` (= 2^(32)) instead, we keep the value positive without changing its truncated value. Without the additional sign bit(s) the result fits into 32bit and the compiler is happy :).
[^Linker 1M]: We don't want to load the kernel to e.g. `0x0` because there are many special memory areas below the 1MB mark (for example the so-called VGA buffer at `0xb8000`, that we use to print `OK` to the screen).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1,929 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="7.2973523in"
height="4.1279249in"
viewBox="-2141 2141 8777.1352 4929.2808"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="X86_Paging_64bit.svg">
<metadata
id="metadata370">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs368" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1080"
inkscape:window-height="1868"
id="namedview366"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.4404005"
inkscape:cx="383.13253"
inkscape:cy="73.778068"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g4" />
<g
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="g4"
transform="translate(-2845,-286.64442)">
<rect
x="8503"
y="4015"
width="708"
height="3307"
rx="0"
style="fill:#dfdfdf"
id="rect8" />
<polyline
points="3365,2598 3365,3070"
id="polyline10" />
<polyline
points="3189,2598 3189,3070"
id="polyline12" />
<polyline
points="3012,2598 3012,3070"
id="polyline14" />
<polyline
points="2834,2598 2834,3070"
id="polyline16" />
<polyline
points="2657,2598 2657,3070"
id="polyline18" />
<polyline
points="2480,2598 2480,3070"
id="polyline20" />
<polyline
points="2303,2598 2303,3070"
id="polyline22" />
<polyline
points="2125,2456 2125,3070"
id="polyline24" />
<polyline
points="1948,2598 1948,3070"
id="polyline26" />
<polyline
points="1772,2598 1772,3070"
id="polyline28" />
<polyline
points="1594,2598 1594,3070"
id="polyline30" />
<polyline
points="1417,2598 1417,3070"
id="polyline32" />
<polyline
points="1239,2598 1239,3070"
id="polyline34" />
<polyline
points="1063,2598 1063,3070"
id="polyline36" />
<polyline
points="886,2598 886,3070"
id="polyline38" />
<polyline
points="708,2456 708,3070"
id="polyline40" />
<polyline
points="3543,2456 3543,3070"
id="polyline74" />
<polyline
points="-2125,2598 3543,2598"
id="polyline76"
transform="matrix(0.49978943,0,0,1,1772.2461,0)" />
<polyline
points="3543,3070 -1948,3070 -2125,3070"
id="polyline78"
transform="matrix(0.49786923,0,0,1,1779.0493,0)" />
<polyline
points="9035,2598 9035,3070"
id="polyline80" />
<polyline
points="8858,2598 8858,3070"
id="polyline82" />
<polyline
points="8681,2598 8681,3070"
id="polyline84" />
<polyline
points="8503,2598 8503,3070"
id="polyline86" />
<polyline
points="8326,2598 8326,3070"
id="polyline88" />
<polyline
points="8150,2598 8150,3070"
id="polyline90" />
<polyline
points="7972,2598 7972,3070"
id="polyline92" />
<polyline
points="7795,2456 7795,3070"
id="polyline94" />
<polyline
points="7617,2598 7617,3070"
id="polyline96" />
<polyline
points="7441,2598 7441,3070"
id="polyline98" />
<polyline
points="7264,2598 7264,3070"
id="polyline100" />
<polyline
points="7086,2598 7086,3070"
id="polyline102" />
<polyline
points="6909,2598 6909,3070"
id="polyline104" />
<polyline
points="6732,2598 6732,3070"
id="polyline106" />
<polyline
points="6555,2598 6555,3070"
id="polyline108" />
<polyline
points="6377,2456 6377,3070"
id="polyline110" />
<polyline
points="6200,2598 6200,3070"
id="polyline112" />
<polyline
points="6024,2598 6024,3070"
id="polyline114" />
<polyline
points="5846,2598 5846,3070"
id="polyline116" />
<polyline
points="5669,2598 5669,3070"
id="polyline118" />
<polyline
points="5491,2598 5491,3070"
id="polyline120" />
<polyline
points="5315,2598 5315,3070"
id="polyline122" />
<polyline
points="5138,2598 5138,3070"
id="polyline124" />
<polyline
points="4960,2409 4960,3070"
id="polyline126" />
<polyline
points="4783,2598 4783,3070"
id="polyline128" />
<polyline
points="4606,2598 4606,3070"
id="polyline130" />
<polyline
points="4429,2598 4429,3070"
id="polyline132" />
<polyline
points="4251,2598 4251,3070"
id="polyline134" />
<polyline
points="4074,2598 4074,3070"
id="polyline136" />
<polyline
points="3898,2598 3898,3070"
id="polyline138" />
<polyline
points="3720,2598 3720,3070"
id="polyline140" />
<polyline
points="9212,2456 9212,3070"
id="polyline142" />
<polyline
points="3543,2598 9212,2598"
id="polyline144" />
<polyline
points="9212,3070 3720,3070 0,3070"
id="polyline146"
transform="matrix(0.92335242,0,0,1,706.07747,0)" />
<rect
x="5102"
y="4488"
width="1181"
height="2362"
rx="0"
style="fill:#dfdfdf"
id="rect148" />
<rect
x="3307"
y="4251"
width="1181"
height="2362"
rx="0"
style="fill:#dfdfdf"
id="rect150" />
<rect
x="6897"
y="4724"
width="1181"
height="2362"
rx="0"
style="fill:#dfdfdf"
id="rect152" />
<rect
x="1511"
y="4015"
width="1181"
height="2362"
rx="0"
style="fill:#dfdfdf"
id="rect154" />
<rect
x="1417"
y="6850"
width="1417"
height="472"
rx="0"
id="rect156" />
<rect
x="8503"
y="5433"
width="708"
height="236"
rx="0"
style="fill:#dfdfdf"
id="rect158" />
<rect
x="5102"
y="5669"
width="1181"
height="472"
rx="0"
id="rect160" />
<rect
x="3307"
y="5433"
width="1181"
height="472"
rx="0"
id="rect162" />
<rect
x="6897"
y="5905"
width="1181"
height="472"
rx="0"
id="rect164" />
<rect
x="1511"
y="5196"
width="1181"
height="472"
rx="0"
id="rect166" />
<circle
cx="6141"
cy="5905"
r="47"
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
id="circle170" />
<circle
cx="4346"
cy="5669"
r="47"
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
id="circle172" />
<circle
cx="7937"
cy="6141"
r="47"
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
id="circle174" />
<circle
cx="2551"
cy="5433"
r="47"
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
id="circle176" />
<polyline
points="3118,3590 2834,3779"
style="stroke:#0000ff"
id="polyline178" />
<polyline
points="1322,3590 1039,3779"
style="stroke:#0000ff"
id="polyline180" />
<polyline
points="4913,3590 4629,3779"
style="stroke:#0000ff"
id="polyline184" />
<polyline
points="6519,3590 6236,3779"
style="stroke:#0000ff"
id="polyline186" />
<polyline
points="8314,3590 8031,3779"
style="stroke:#0000ff"
id="polyline188" />
<circle
cx="1653"
cy="7086"
r="47"
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
id="circle190" />
<polyline
points="1653,7086 1181,7086 1181,6377 1354,6377"
style="stroke:#0000ff"
id="polyline192" />
<polygon
points="1512,6377 1355,6330 1355,6425 1355,6425 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon194" />
<polyline
points="7937,6141 8173,6141 8173,7322 8346,7322"
style="stroke:#0000ff"
id="polyline196" />
<polygon
points="8504,7322 8347,7275 8347,7370 8347,7370 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon198" />
<polyline
points="8173,3543 8173,5669 8346,5669"
style="stroke:#0000ff"
id="polyline200" />
<polygon
points="8504,5669 8347,5622 8347,5716 8347,5716 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon202" />
<polyline
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
style="stroke:#0000ff"
id="polyline204" />
<polyline
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
style="stroke:#0000ff"
id="polyline206" />
<polyline
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
style="stroke:#0000ff"
id="polyline208" />
<polyline
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
style="stroke:#0000ff"
id="polyline210" />
<polyline
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
style="stroke:#0000ff"
id="polyline212" />
<polyline
points="4771,3543 4771,6141 4944,6141"
style="stroke:#0000ff"
id="polyline214" />
<polygon
points="5103,6141 4945,6094 4945,6188 4945,6188 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon216" />
<polyline
points="6377,3543 6377,4015 6519,4157 6519,6377 6740,6377"
style="stroke:#0000ff"
id="polyline218" />
<polygon
points="6898,6377 6741,6330 6741,6425 6741,6425 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon220" />
<polyline
points="6141,5905 6377,5905 6377,7086 6740,7086"
style="stroke:#0000ff"
id="polyline222" />
<polygon
points="6898,7086 6741,7039 6741,7133 6741,7133 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon224" />
<polyline
points="4346,5669 4629,5669 4629,6850 4944,6850"
style="stroke:#0000ff"
id="polyline226" />
<polygon
points="5103,6850 4945,6803 4945,6897 4945,6897 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon228" />
<polyline
points="2598,5433 2834,5433 2834,6614 3149,6614"
style="stroke:#0000ff"
id="polyline230" />
<polygon
points="3308,6614 3150,6566 3150,6661 3150,6661 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon232" />
<polyline
points="1181,3543 1181,5669 1401,5669"
style="stroke:#0000ff"
id="polyline234" />
<polygon
points="1560,5669 1402,5622 1402,5716 1402,5716 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon236" />
<polyline
points="2976,3543 2976,5905 3149,5905"
style="stroke:#0000ff"
id="polyline238" />
<polygon
points="3308,5905 3150,5858 3150,5952 3150,5952 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon240" />
<g
style="fill:#000000;stroke-width:0"
id="g242">
<text
xml:space="preserve"
x="2281.8145"
y="7165"
font-style="normal"
font-weight="normal"
font-size="152"
id="text244"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">CR3 register</text>
<text
xml:space="preserve"
x="3507"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text246"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
<text
xml:space="preserve"
x="2161"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text248"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
<text
xml:space="preserve"
x="2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text250"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
<text
xml:space="preserve"
x="744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text252"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
<text
xml:space="preserve"
x="672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text254"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text256"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="-744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text258"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text260"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="9176"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text262"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
<text
xml:space="preserve"
x="7759"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text264"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
<text
xml:space="preserve"
x="6342"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text266"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
<text
xml:space="preserve"
x="4924"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text268"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
<text
xml:space="preserve"
x="3579"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text270"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
<text
xml:space="preserve"
x="6413"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text272"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
<text
xml:space="preserve"
x="7830"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text274"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
<text
xml:space="preserve"
x="4996"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text276"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
<g
transform="matrix(0,-1,1,0,8976,4724)"
id="g278">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text280"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<g
transform="matrix(0,-1,1,0,8976,6614)"
id="g282">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text284"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<g
transform="matrix(0,-1,1,0,9448,5622)"
id="g286">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="152"
id="text288"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46390561px"
id="tspan3509">4K memory page</tspan></text>
</g>
<text
xml:space="preserve"
x="-2125"
y="2314"
font-style="normal"
font-weight="normal"
font-size="152"
id="text290"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<text
xml:space="preserve"
x="5622"
y="5958.457"
font-style="normal"
font-weight="normal"
font-size="152"
id="text292"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46391296px"
id="tspan3497">P2 entry</tspan></text>
<g
transform="matrix(0,-1,1,0,5723,6614)"
id="g296">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text298"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<g
transform="matrix(0,-1,1,0,5723,5196)"
id="g300">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text302"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="5692"
y="4393"
font-style="normal"
font-weight="normal"
font-size="152"
id="text304"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46390561px"
id="tspan3505">P2 table</tspan></text>
<g
transform="matrix(0,-1,1,0,3928,6377)"
id="g306">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<g
transform="matrix(0,-1,1,0,3928,4960)"
id="g310">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text312"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="3826"
y="5722.457"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46391296px"
id="tspan3495">P3 entry</tspan></text>
<text
xml:space="preserve"
x="3874"
y="3968"
font-style="normal"
font-weight="normal"
font-size="152"
id="text318"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
xml:space="preserve"
x="3874"
y="4157"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46390561px"
id="tspan3503">P3 table</tspan></text>
<text
xml:space="preserve"
x="7417"
y="6194.457"
font-style="normal"
font-weight="normal"
font-size="152"
id="text322"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46391296px"
id="tspan3499">P1 entry </tspan></text>
<g
transform="matrix(0,-1,1,0,7519,6850)"
id="g326">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text328"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<g
transform="matrix(0,-1,1,0,7519,5433)"
id="g330">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text332"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="7487"
y="4629"
font-style="normal"
font-weight="normal"
font-size="152"
id="text334"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46390561px"
id="tspan3507">P1 table</tspan></text>
<g
transform="matrix(0,-1,1,0,2133,6141)"
id="g336">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text338"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<g
transform="matrix(0,-1,1,0,2133,4724)"
id="g340">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text342"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
x="2004.2715"
y="5486.457"
font-style="normal"
font-weight="normal"
font-size="152"
id="text344"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">
<tspan
style="font-size:200.46390561px"
id="tspan3493">P4 entry</tspan>
</text>
<text
x="2031"
y="5622"
font-style="normal"
font-weight="normal"
font-size="152"
id="text346"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
x="2078"
y="3941.2715"
font-style="normal"
font-weight="normal"
font-size="152"
id="text348"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">
<tspan
style="font-size:200.46391296px"
id="tspan3501">P4 table</tspan>
</text>
<text
x="3165"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text350"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
<text
x="1370"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text352"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
<text
x="1370"
y="6708"
font-style="normal"
font-weight="normal"
font-size="152"
id="text354"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
<text
x="4960"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text356"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
<text
x="6566"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text358"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
<text
x="8362"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text360"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
<text
x="1181"
y="7532.2715"
font-style="normal"
font-weight="normal"
font-size="152"
id="text364"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -1,525 +0,0 @@
+++
title = "Entering Long Mode"
weight = 2
path = "entering-longmode"
aliases = ["entering-longmode.html", "/2015/08/25/entering-longmode/", "/rust-os/entering-longmode.html"]
date = 2015-08-25
template = "edition-1/page.html"
[extra]
updated = "2015-10-29"
+++
In the [previous post] we created a minimal multiboot kernel. It just prints `OK` and hangs. The goal is to extend it and call 64-bit [Rust] code. But the CPU is currently in [protected mode] and allows only 32-bit instructions and up to 4GiB memory. So we need to set up _Paging_ and switch to the 64-bit [long mode] first.
[previous post]: @/edition-1/posts/01-multiboot-kernel/index.md
[Rust]: https://www.rust-lang.org/
[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
[long mode]: https://en.wikipedia.org/wiki/Long_mode
<!-- more -->
I tried to explain everything in detail and to keep the code as simple as possible. If you have any questions, suggestions, or issues, please leave a comment or [create an issue] on Github. The source code is available in a [repository][source code], too.
[create an issue]: https://github.com/phil-opp/blog_os/issues
[source code]: https://github.com/phil-opp/blog_os/tree/first_edition_post_2/src/arch/x86_64
## Some Tests
To avoid bugs and strange errors on old CPUs we should check if the processor supports every needed feature. If not, the kernel should abort and display an error message. To handle errors easily, we create an error procedure in `boot.asm`. It prints a rudimentary `ERR: X` message, where X is an error code letter, and hangs:
```nasm
; Prints `ERR: ` and the given error code to screen and hangs.
; parameter: error code (in ascii) in al
error:
mov dword [0xb8000], 0x4f524f45
mov dword [0xb8004], 0x4f3a4f52
mov dword [0xb8008], 0x4f204f20
mov byte [0xb800a], al
hlt
```
At address `0xb8000` begins the so-called [VGA text buffer]. It's an array of screen characters that are displayed by the graphics card. A [future post] will cover the VGA buffer in detail and create a Rust interface to it. But for now, manual bit-fiddling is the easiest option.
[VGA text buffer]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
[future post]: @/edition-1/posts/04-printing-to-screen/index.md
A screen character consists of a 8 bit color code and a 8 bit [ASCII] character. We used the color code `4f` for all characters, which means white text on red background. `0x52` is an ASCII `R`, `0x45` is an `E`, `0x3a` is a `:`, and `0x20` is a space. The second space is overwritten by the given ASCII byte. Finally the CPU is stopped with the `hlt` instruction.
[ASCII]: https://en.wikipedia.org/wiki/ASCII
Now we can add some check _functions_. A function is just a normal label with an `ret` (return) instruction at the end. The `call` instruction can be used to call it. Unlike the `jmp` instruction that just jumps to a memory address, the `call` instruction will push a return address to the stack (and the `ret` will jump to this address). But we don't have a stack yet. The [stack pointer] in the esp register could point to some important data or even invalid memory. So we need to update it and point it to some valid stack memory.
[stack pointer]: https://stackoverflow.com/a/1464052/866447
### Creating a Stack
To create stack memory we reserve some bytes at the end of our `boot.asm`:
```nasm
...
section .bss
stack_bottom:
resb 64
stack_top:
```
A stack doesn't need to be initialized because we will `pop` only when we `pushed` before. So storing the stack memory in the executable file would make it unnecessary large. By using the [.bss] section and the `resb` (reserve byte) command, we just store the length of the uninitialized data (= 64). When loading the executable, GRUB will create the section of required size in memory.
[.bss]: https://en.wikipedia.org/wiki/.bss
To use the new stack, we update the stack pointer register right after `start`:
```nasm
global start
section .text
bits 32
start:
mov esp, stack_top
; print `OK` to screen
...
```
We use `stack_top` because the stack grows downwards: A `push eax` subtracts 4 from `esp` and does a `mov [esp], eax` afterwards (`eax` is a general purpose register).
Now we have a valid stack pointer and are able to call functions. The following check functions are just here for completeness and I won't explain details. Basically they all work the same: They will check for a feature and jump to `error` if it's not available.
### Multiboot check
We rely on some Multiboot features in the next posts. To make sure the kernel was really loaded by a Multiboot compliant bootloader, we can check the `eax` register. According to the Multiboot specification ([PDF][Multiboot specification]), the bootloader must write the magic value `0x36d76289` to it before loading a kernel. To verify that we can add a simple function:
```nasm
check_multiboot:
cmp eax, 0x36d76289
jne .no_multiboot
ret
.no_multiboot:
mov al, "0"
jmp error
```
We use the `cmp` instruction to compare the value in `eax` to the magic value. If the values are equal, the `cmp` instruction sets the zero flag in the [FLAGS register]. The `jne` (“jump if not equal”) instruction reads this zero flag and jumps to the given address if it's not set. Thus we jump to the `.no_multiboot` label if `eax` does not contain the magic value.
In `no_multiboot`, we use the `jmp` (“jump”) instruction to jump to our error function. We could just as well use the `call` instruction, which additionally pushes the return address. But the return address is not needed because `error` never returns. To pass `0` as error code to the `error` function, we move it into `al` before the jump (`error` will read it from there).
[Multiboot specification]: https://nongnu.askapache.com/grub/phcoder/multiboot.pdf
[FLAGS register]: https://en.wikipedia.org/wiki/FLAGS_register
### CPUID check
[CPUID] is a CPU instruction that can be used to get various information about the CPU. But not every processor supports it. CPUID detection is quite laborious, so we just copy a detection function from the [OSDev wiki][CPUID detection]:
[CPUID]: https://wiki.osdev.org/CPUID
[CPUID detection]: https://wiki.osdev.org/Setting_Up_Long_Mode#Detection_of_CPUID
```nasm
check_cpuid:
; Check if CPUID is supported by attempting to flip the ID bit (bit 21)
; in the FLAGS register. If we can flip it, CPUID is available.
; Copy FLAGS in to EAX via stack
pushfd
pop eax
; Copy to ECX as well for comparing later on
mov ecx, eax
; Flip the ID bit
xor eax, 1 << 21
; Copy EAX to FLAGS via the stack
push eax
popfd
; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported)
pushfd
pop eax
; Restore FLAGS from the old version stored in ECX (i.e. flipping the
; ID bit back if it was ever flipped).
push ecx
popfd
; Compare EAX and ECX. If they are equal then that means the bit
; wasn't flipped, and CPUID isn't supported.
cmp eax, ecx
je .no_cpuid
ret
.no_cpuid:
mov al, "1"
jmp error
```
Basically, the `CPUID` instruction is supported if we can flip some bit in the [FLAGS register]. We can't operate on the flags register directly, so we need to load it into some general purpose register such as `eax` first. The only way to do this is to push the `FLAGS` register on the stack through the `pushfd` instruction and then pop it into `eax`. Equally, we write it back through `push ecx` and `popfd`. To flip the bit we use the `xor` instruction to perform an [exclusive OR]. Finally we compare the two values and jump to `.no_cpuid` if both are equal (`je` “jump if equal”). The `.no_cpuid` code just jumps to the `error` function with error code `1`.
Don't worry, you don't need to understand the details.
[exclusive OR]: https://en.wikipedia.org/wiki/Exclusive_or
### Long Mode check
Now we can use CPUID to detect whether long mode can be used. I use code from [OSDev][long mode detection] again:
[long mode detection]: https://wiki.osdev.org/Setting_Up_Long_Mode#x86_or_x86-64
```nasm
check_long_mode:
; test if extended processor info in available
mov eax, 0x80000000 ; implicit argument for cpuid
cpuid ; get highest supported argument
cmp eax, 0x80000001 ; it needs to be at least 0x80000001
jb .no_long_mode ; if it's less, the CPU is too old for long mode
; use extended info to test if long mode is available
mov eax, 0x80000001 ; argument for extended processor info
cpuid ; returns various feature bits in ecx and edx
test edx, 1 << 29 ; test if the LM-bit is set in the D-register
jz .no_long_mode ; If it's not set, there is no long mode
ret
.no_long_mode:
mov al, "2"
jmp error
```
Like many low-level things, CPUID is a bit strange. Instead of taking a parameter, the `cpuid` instruction implicitly uses the `eax` register as argument. To test if long mode is available, we need to call `cpuid` with `0x80000001` in `eax`. This loads some information to the `ecx` and `edx` registers. Long mode is supported if the 29th bit in `edx` is set. [Wikipedia][cpuid long mode] has detailed information.
[cpuid long mode]: https://en.wikipedia.org/wiki/CPUID#EAX.3D80000001h:_Extended_Processor_Info_and_Feature_Bits
If you look at the assembly above, you'll probably notice that we call `cpuid` twice. The reason is that the CPUID command started with only a few functions and was extended over time. So old processors may not know the `0x80000001` argument at all. To test if they do, we need to invoke `cpuid` with `0x80000000` in `eax` first. It returns the highest supported parameter value in `eax`. If it's at least `0x80000001`, we can test for long mode as described above. Else the CPU is old and doesn't know what long mode is either. In that case, we directly jump to `.no_long_mode` through the `jb` instruction (“jump if below”).
### Putting it together
We just call these check functions right after start:
```nasm
global start
section .text
bits 32
start:
mov esp, stack_top
call check_multiboot
call check_cpuid
call check_long_mode
; print `OK` to screen
...
```
When the CPU doesn't support a needed feature, we get an error message with an unique error code. Now we can start the real work.
## Paging
_Paging_ is a memory management scheme that separates virtual and physical memory. The address space is split into equal sized _pages_ and a _page table_ specifies which virtual page points to which physical page. If you never heard of paging, you might want to look at the paging introduction ([PDF][paging chapter]) of the [Three Easy Pieces] OS book.
[paging chapter]: http://pages.cs.wisc.edu/~remzi/OSTEP/vm-paging.pdf
[Three Easy Pieces]: http://pages.cs.wisc.edu/~remzi/OSTEP/
In long mode, x86 uses a page size of 4096 bytes and a 4 level page table that consists of:
- the Page-Map Level-4 Table (PML4),
- the Page-Directory Pointer Table (PDP),
- the Page-Directory Table (PD),
- and the Page Table (PT).
As I don't like these names, I will call them P4, P3, P2, and P1 from now on.
Each page table contains 512 entries and one entry is 8 bytes, so they fit exactly in one page (`512*8 = 4096`). To translate a virtual address to a physical address the CPU[^hardware_lookup] will do the following[^virtual_physical_translation_source]:
![translation of virtual to physical addresses in 64 bit mode](X86_Paging_64bit.svg)
1. Get the address of the P4 table from the CR3 register
2. Use bits 39-47 (9 bits) as an index into P4 (`2^9 = 512 = number of entries`)
3. Use the following 9 bits as an index into P3
4. Use the following 9 bits as an index into P2
5. Use the following 9 bits as an index into P1
6. Use the last 12 bits as page offset (`2^12 = 4096 = page size`)
But what happens to bits 48-63 of the 64-bit virtual address? Well, they can't be used. The “64-bit” long mode is in fact just a 48-bit mode. The bits 48-63 must be copies of bit 47, so each valid virtual address is still unique. For more information see [Wikipedia][wikipedia_48bit_mode].
[wikipedia_48bit_mode]: https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details
An entry in the P4, P3, P2, and P1 tables consists of the page aligned 52-bit _physical_ address of the frame or the next page table and the following bits that can be OR-ed in:
Bit(s) | Name | Meaning
--------------------- | ------ | ----------------------------------
0 | present | the page is currently in memory
1 | writable | it's allowed to write to this page
2 | user accessible | if not set, only kernel mode code can access this page
3 | write through caching | writes go directly to memory
4 | disable cache | no cache is used for this page
5 | accessed | the CPU sets this bit when this page is used
6 | dirty | the CPU sets this bit when a write to this page occurs
7 | huge page/null | must be 0 in P1 and P4, creates a 1GiB page in P3, creates a 2MiB page in P2
8 | global | page isn't flushed from caches on address space switch (PGE bit of CR4 register must be set)
9-11 | available | can be used freely by the OS
52-62 | available | can be used freely by the OS
63 | no execute | forbid executing code on this page (the NXE bit in the EFER register must be set)
### Set Up Identity Paging
When we switch to long mode, paging will be activated automatically. The CPU will then try to read the instruction at the following address, but this address is now a virtual address. So we need to do _identity mapping_, i.e. map a physical address to the same virtual address.
The `huge page` bit is now very useful to us. It creates a 2MiB (when used in P2) or even a 1GiB page (when used in P3). So we could map the first _gigabytes_ of the kernel with only one P4 and one P3 table by using 1GiB pages. Unfortunately 1GiB pages are relatively new feature, for example Intel introduced it 2010 in the [Westmere architecture]. Therefore we will use 2MiB pages instead to make our kernel compatible to older computers, too.
[Westmere architecture]: https://en.wikipedia.org/wiki/Westmere_(microarchitecture)#Technology
To identity map the first gigabyte of our kernel with 512 2MiB pages, we need one P4, one P3, and one P2 table. Of course we will replace them with finer-grained tables later. But now that we're stuck with assembly, we choose the easiest way.
We can add these two tables at the beginning[^page_table_alignment] of the `.bss` section:
```nasm
...
section .bss
align 4096
p4_table:
resb 4096
p3_table:
resb 4096
p2_table:
resb 4096
stack_bottom:
resb 64
stack_top:
```
The `resb` command reserves the specified amount of bytes without initializing them, so the 8KiB don't need to be saved in the executable. The `align 4096` ensures that the page tables are page aligned.
When GRUB creates the `.bss` section in memory, it will initialize it to `0`. So the `p4_table` is already valid (it contains 512 non-present entries) but not very useful. To be able to map 2MiB pages, we need to link P4's first entry to the `p3_table` and P3's first entry to the the `p2_table`:
```nasm
set_up_page_tables:
; map first P4 entry to P3 table
mov eax, p3_table
or eax, 0b11 ; present + writable
mov [p4_table], eax
; map first P3 entry to P2 table
mov eax, p2_table
or eax, 0b11 ; present + writable
mov [p3_table], eax
; TODO map each P2 entry to a huge 2MiB page
ret
```
We just set the present and writable bits (`0b11` is a binary number) in the aligned P3 table address and move it to the first 4 bytes of the P4 table. Then we do the same to link the first P3 entry to the `p2_table`.
Now we need to map P2's first entry to a huge page starting at 0, P2's second entry to a huge page starting at 2MiB, P2's third entry to a huge page starting at 4MiB, and so on. It's time for our first (and only) assembly loop:
```nasm
set_up_page_tables:
...
; map each P2 entry to a huge 2MiB page
mov ecx, 0 ; counter variable
.map_p2_table:
; map ecx-th P2 entry to a huge page that starts at address 2MiB*ecx
mov eax, 0x200000 ; 2MiB
mul ecx ; start address of ecx-th page
or eax, 0b10000011 ; present + writable + huge
mov [p2_table + ecx * 8], eax ; map ecx-th entry
inc ecx ; increase counter
cmp ecx, 512 ; if counter == 512, the whole P2 table is mapped
jne .map_p2_table ; else map the next entry
ret
```
Maybe I should first explain how an assembly loop works. We use the `ecx` register as a counter variable, just like `i` in a for loop. After mapping the `ecx-th` entry, we increase `ecx` by one and jump to `.map_p2_table` again if it's still smaller than 512.
To map a P2 entry we first calculate the start address of its page in `eax`: The `ecx-th` entry needs to be mapped to `ecx * 2MiB`. We use the `mul` operation for that, which multiplies `eax` with the given register and stores the result in `eax`. Then we set the `present`, `writable`, and `huge page` bits and write it to the P2 entry. The address of the `ecx-th` entry in P2 is `p2_table + ecx * 8`, because each entry is 8 bytes large.
Now the first gigabyte (512 * 2MiB) of our kernel is identity mapped and thus accessible through the same physical and virtual addresses.
### Enable Paging
To enable paging and enter long mode, we need to do the following:
1. write the address of the P4 table to the CR3 register (the CPU will look there, see the [paging section](#paging))
2. long mode is an extension of [Physical Address Extension] \(PAE), so we need to enable PAE first
3. Set the long mode bit in the EFER register
4. Enable Paging
[Physical Address Extension]: https://en.wikipedia.org/wiki/Physical_Address_Extension
The assembly function looks like this (some boring bit-moving to various registers):
```nasm
enable_paging:
; load P4 to cr3 register (cpu uses this to access the P4 table)
mov eax, p4_table
mov cr3, eax
; enable PAE-flag in cr4 (Physical Address Extension)
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
; set the long mode bit in the EFER MSR (model specific register)
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr
; enable paging in the cr0 register
mov eax, cr0
or eax, 1 << 31
mov cr0, eax
ret
```
The `or eax, 1 << X` is a common pattern. It sets the bit `X` in the eax register (`<<` is a left shift). Through `rdmsr` and `wrmsr` it's possible to read/write to the so-called model specific registers at address `ecx` (in this case `ecx` points to the EFER register).
Finally we need to call our new functions in `start`:
```nasm
...
start:
mov esp, stack_top
call check_multiboot
call check_cpuid
call check_long_mode
call set_up_page_tables ; new
call enable_paging ; new
; print `OK` to screen
mov dword [0xb8000], 0x2f4b2f4f
hlt
...
```
To test it we execute `make run`. If the green OK is still printed, we have successfully enabled paging!
## The Global Descriptor Table
After enabling Paging, the processor is in long mode. So we can use 64-bit instructions now, right? Wrong. The processor is still in a 32-bit compatibility submode. To actually execute 64-bit code, we need to set up a new Global Descriptor Table.
The Global Descriptor Table (GDT) was used for _Segmentation_ in old operating systems. I won't explain Segmentation but the [Three Easy Pieces] OS book has good introduction ([PDF][Segmentation chapter]) again.
[Segmentation chapter]: http://pages.cs.wisc.edu/~remzi/OSTEP/vm-segmentation.pdf
Today almost everyone uses Paging instead of Segmentation (and so do we). But on x86, a GDT is always required, even when you're not using Segmentation. GRUB has set up a valid 32-bit GDT for us but now we need to switch to a long mode GDT.
A GDT always starts with a 0-entry and contains an arbitrary number of segment entries afterwards. A 64-bit entry has the following format:
Bit(s) | Name | Meaning
--------------------- | ------ | ----------------------------------
0-41 | ignored | ignored in 64-bit mode
42 | conforming | the current privilege level can be higher than the specified level for code segments (else it must match exactly)
43 | executable | if set, it's a code segment, else it's a data segment
44 | descriptor type | should be 1 for code and data segments
45-46 | privilege | the [ring level]: 0 for kernel, 3 for user
47 | present | must be 1 for valid selectors
48-52 | ignored | ignored in 64-bit mode
53 | 64-bit | should be set for 64-bit code segments
54 | 32-bit | must be 0 for 64-bit segments
55-63 | ignored | ignored in 64-bit mode
[ring level]: https://wiki.osdev.org/Security#Rings
We need one code segment, a data segment is not necessary in 64-bit mode. Code segments have the following bits set: _descriptor type_, _present_, _executable_ and the _64-bit_ flag. Translated to assembly the long mode GDT looks like this:
```nasm
section .rodata
gdt64:
dq 0 ; zero entry
dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment
```
We chose the `.rodata` section here because it's initialized read-only data. The `dq` command stands for `define quad` and outputs a 64-bit constant (similar to `dw` and `dd`). And the `(1<<43)` is a bit shift that sets bit 43.
### Loading the GDT
To load our new 64-bit GDT, we have to tell the CPU its address and length. We do this by passing the memory location of a special pointer structure to the `lgdt` (load GDT) instruction. The pointer structure looks like this:
```nasm
gdt64:
dq 0 ; zero entry
dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment
.pointer:
dw $ - gdt64 - 1
dq gdt64
```
The first 2 bytes specify the (GDT length - 1). The `$` is a special symbol that is replaced with the current address (it's equal to `.pointer` in our case). The following 8 bytes specify the GDT address. Labels that start with a point (such as `.pointer`) are sub-labels of the last label without point. To access them, they must be prefixed with the parent label (e.g., `gdt64.pointer`).
Now we can load the GDT in `start`:
```nasm
start:
...
call enable_paging
; load the 64-bit GDT
lgdt [gdt64.pointer]
; print `OK` to screen
...
```
When you still see the green `OK`, everything went fine and the new GDT is loaded. But we still can't execute 64-bit code: The code selector register `cs` still has the values from the old GDT. To update it, we need to load it with the GDT offset (in bytes) of the desired segment. In our case the code segment starts at byte 8 of the GDT, but we don't want to hardcode that 8 (in case we modify our GDT later). Instead, we add a `.code` label to our GDT, that calculates the offset directly from the GDT:
```nasm
section .rodata
gdt64:
dq 0 ; zero entry
.code: equ $ - gdt64 ; new
dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment
.pointer:
...
```
We can't just use a normal label here, since we need the table _offset_. We calculate this offset using the current address `$` and set the label to this value using [equ]. Now we can use `gdt64.code` instead of 8 and this label will still work if we modify the GDT.
[equ]: https://www.nasm.us/doc/nasmdoc3.html#section-3.2.4
In order to finally enter the true 64-bit mode, we need to load `cs` with `gdt64.code`. But we can't do it through `mov`. The only way to reload the code selector is a _far jump_ or a _far return_. These instructions work like a normal jump/return but change the code selector. We use a far jump to a long mode label:
```nasm
global start
extern long_mode_start
...
start:
...
lgdt [gdt64.pointer]
jmp gdt64.code:long_mode_start
...
```
The actual `long_mode_start` label is defined as `extern`, so it's part of another file. The `jmp gdt64.code:long_mode_start` is the mentioned far jump.
I put the 64-bit code into a new file to separate it from the 32-bit code, thereby we can't call the (now invalid) 32-bit code accidentally. The new file (I named it `long_mode_init.asm`) looks like this:
```nasm
global long_mode_start
section .text
bits 64
long_mode_start:
; print `OKAY` to screen
mov rax, 0x2f592f412f4b2f4f
mov qword [0xb8000], rax
hlt
```
You should see a green `OKAY` on the screen. Some notes on this last step:
- As the CPU expects 64-bit instructions now, we use `bits 64`
- We can now use the extended registers. Instead of the 32-bit `eax`, `ebx`, etc. we now have the 64-bit `rax`, `rbx`, …
- and we can write these 64-bit registers directly to memory using `mov qword` (quad word)
_Congratulations_! You have successfully wrestled through this CPU configuration and compatibility mode mess :).
#### One Last Thing
Above, we reloaded the code segment register `cs` with the new GDT offset. However, the data segment registers `ss`, `ds`, `es`, `fs`, and `gs` still contain the data segment offsets of the old GDT. This isn't necessarily bad, since they're ignored by almost all instructions in 64-bit mode. However, there are a few instructions that expect a valid data segment descriptor _or the null descriptor_ in those registers. An example is the the [iretq] instruction that we'll need in the [_Returning from Exceptions_] post.
[iretq]: @/edition-1/extra/naked-exceptions/03-returning-from-exceptions/index.md#the-iretq-instruction
[_Returning from Exceptions_]: @/edition-1/extra/naked-exceptions/03-returning-from-exceptions/index.md
To avoid future problems, we reload all data segment registers with null:
```nasm
long_mode_start:
; load 0 into all data segment registers
mov ax, 0
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; print `OKAY` to screen
...
```
## What's next?
It's time to finally leave assembly behind and switch to [Rust]. Rust is a systems language without garbage collections that guarantees memory safety. Through a real type system and many abstractions it feels like a high-level language but can still be low-level enough for OS development. The [next post] describes the Rust setup.
[Rust]: https://www.rust-lang.org/
[next post]: @/edition-1/posts/03-set-up-rust/index.md
## Footnotes
[^hardware_lookup]: In the x86 architecture, the page tables are _hardware walked_, so the CPU will look at the table on its own when it needs a translation. Other architectures, for example MIPS, just throw an exception and let the OS translate the virtual address.
[^virtual_physical_translation_source]: Image source: [Wikipedia](https://commons.wikimedia.org/wiki/File:X86_Paging_64bit.svg), with modified font size, page table naming, and removed sign extended bits. The modified file is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
[^page_table_alignment]: Page tables need to be page-aligned as the bits 0-11 are used for flags. By putting these tables at the beginning of `.bss`, the linker can just page align the whole section and we don't have unused padding bytes in between.

View File

@@ -1,491 +0,0 @@
+++
title = "Set Up Rust"
weight = 3
path = "set-up-rust"
aliases = ["set-up-rust.html", "setup-rust.html", "/2015/09/02/setup-rust/", "/rust-os/setup-rust.html"]
date = 2015-09-02
template = "edition-1/page.html"
[extra]
updated = "2017-04-12"
+++
In the previous posts we created a [minimal Multiboot kernel][multiboot post] and [switched to Long Mode][long mode post]. Now we can finally switch to [Rust] code. Rust is a high-level language without runtime. It allows us to not link the standard library and write bare metal code. Unfortunately the setup is not quite hassle-free yet.
[multiboot post]: @/edition-1/posts/01-multiboot-kernel/index.md
[long mode post]: @/edition-1/posts/02-entering-longmode/index.md
[Rust]: https://www.rust-lang.org/
<!-- more -->
This blog post tries to set up Rust step-by-step and point out the different problems. If you have any questions, problems, or suggestions please [file an issue] or create a comment at the bottom. The code from this post is in a [Github repository], too.
[file an issue]: https://github.com/phil-opp/blog_os/issues
[Github repository]: https://github.com/phil-opp/blog_os/tree/first_edition_post_3
## Installing Rust
We need a nightly compiler, as we will use many unstable features. To manage Rust installations I highly recommend [rustup]. It allows you to install nightly, beta, and stable compilers side-by-side and makes it easy to update them. To use a nightly compiler for the current directory, you can run `rustup override add nightly`. Alternatively, you can add a file called `rust-toolchain` to the project's root directory:
```
nightly
```
[rustup]: https://www.rustup.rs/
## Creating a Cargo project
[Cargo] is Rust's excellent package manager. Normally you would call `cargo new` when you want to create a new project folder. We can't use it because our folder already exists, so we need to do it manually. Fortunately we only need to add a cargo configuration file named `Cargo.toml`:
[Cargo]: https://doc.crates.io/guide.html
```toml
[package]
name = "blog_os"
version = "0.1.0"
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
[lib]
crate-type = ["staticlib"]
```
The `package` section contains required project metadata such as the [semantic crate version]. The `lib` section specifies that we want to build a static library, i.e. a library that contains all of its dependencies. This is required to link the Rust project with our kernel.
[semantic crate version]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-package-section
Now we place our root source file in `src/lib.rs`:
```rust
#![feature(lang_items)]
#![no_std]
#[no_mangle]
pub extern fn rust_main() {}
#[lang = "eh_personality"] #[no_mangle] pub extern fn eh_personality() {}
#[lang = "panic_fmt"] #[no_mangle] pub extern fn panic_fmt() -> ! {loop{}}
```
Let's break it down:
- `#!` defines an [attribute] of the current module. Since we are at the root module, the attributes apply to the crate itself.
- The `feature` attribute is used to allow the specified _feature-gated_ attributes in this crate. You can't do that in a stable/beta compiler, so this is one reason we need a Rust nighly.
- The `no_std` attribute prevents the automatic linking of the standard library. We can't use `std` because it relies on operating system features like files, system calls, and various device drivers. Remember that currently the only “feature” of our OS is printing `OKAY` :).
- A `#` without a `!` afterwards defines an attribute for the _following_ item (a function in our case).
- The `no_mangle` attribute disables the automatic [name mangling] that Rust uses to get unique function names. We want to do a `call rust_main` from our assembly code, so this function name must stay as it is.
- We mark our main function as `extern` to make it compatible to the standard C [calling convention].
- The `lang` attribute defines a Rust [language item].
- The `eh_personality` function is used for Rust's [unwinding] on `panic!`. We can leave it empty since we don't have any unwinding support in our OS yet.
- The `panic_fmt` function is the entry point on panic. Right now we can't do anything useful, so we just make sure that it doesn't return (required by the `!` return type).
[attribute]: https://doc.rust-lang.org/book/attributes.html
[name mangling]: https://en.wikipedia.org/wiki/Name_mangling
[calling convention]: https://en.wikipedia.org/wiki/Calling_convention
[language item]: https://doc.rust-lang.org/1.10.0/book/lang-items.html
[unwinding]: https://doc.rust-lang.org/nomicon/unwinding.html
## Building Rust
We can now build it using `cargo build`, which creates a static library at `target/debug/libblog_os.a`. However, the resulting library is specific to our _host_ operating system. This is undesirable, because our target system might be different.
Let's define some properties of our target system:
- **x86_64**: Our target CPU is a recent `x86_64` CPU.
- **No operating system**: Our target does not run any operating system (we're currently writing it), so the compiler should not assume any OS-specific functionality.
- **Handles hardware interrupts**: We're writing a kernel, so we'll need to handle asynchronous hardware interrupts at some point. This means that we have to disable a certain stack pointer optimization (the so-called [red zone]), because it would cause stack corruptions otherwise.
- **No SSE**: Our target might not have [SSE] support. Even if it does, we probably don't want to use SSE instructions in our kernel, because it makes interrupt handling much slower. We will explain this in detail in the [“Handling Exceptions”] post.
- **No hardware floats**: The `x86_64` architecture uses SSE instructions for floating point operations, which we don't want to use (see the previous point). So we also need to avoid hardware floating point operations in our kernel. Instead, we will use _soft floats_, which are basically software functions that emulate floating point operations using normal integers.
[“Handling Exceptions”]: @/edition-1/posts/09-handling-exceptions/index.md
### Target Specifications
Rust allows us to define [custom targets] through a JSON configuration file. A minimal target specification equal to `x86_64-unknown-linux-gnu` (the default 64-bit Linux target) looks like this:
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"linker-flavor": "gcc",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"arch": "x86_64",
"os": "linux"
}
```
[custom targets]: https://doc.rust-lang.org/1.1.0/rustc_back/target/
The `llvm-target` field specifies the target triple that is passed to LLVM. [Target triples] are a naming convention that define the CPU architecture (e.g., `x86_64` or `arm`), the vendor (e.g., `apple` or `unknown`), the operating system (e.g., `windows` or `linux`), and the [ABI] \(e.g., `gnu` or `msvc`). For example, the target triple for 64-bit Linux is `x86_64-unknown-linux-gnu` and for 32-bit Windows the target triple is `i686-pc-windows-msvc`.
[Target triples]: https://llvm.org/docs/LangRef.html#target-triple
[ABI]: https://en.wikipedia.org/wiki/Application_binary_interface
The `data-layout` field is also passed to LLVM and specifies how data should be laid out in memory. It consists of various specifications separated by a `-` character. For example, the `e` means little endian and `S128` specifies that the stack should be 128 bits (= 16 byte) aligned. The format is described in detail in the [LLVM documentation][data layout] but there shouldn't be a reason to change this string.
The `linker-flavor` field was recently introduced in [#40018] with the intention to add support for the LLVM linker [LLD], which is platform independent. In the future, this might allow easy cross compilation without the need to install a gcc cross compiler for linking.
[#40018]: https://github.com/rust-lang/rust/pull/40018
[LLD]: https://lld.llvm.org/
The other fields are used for conditional compilation. This allows crate authors to use `cfg` variables to write special code for depending on the OS or the architecture. There isn't any up-to-date documentation about these fields but the [corresponding source code][target specification] is quite readable.
[data layout]: https://llvm.org/docs/LangRef.html#data-layout
[target specification]: https://github.com/rust-lang/rust/blob/c772948b687488a087356cb91432425662e034b9/src/librustc_back/target/mod.rs#L194-L214
### A Kernel Target Specification
For our target system, we define the following JSON configuration in a file named `x86_64-blog_os.json`:
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"linker-flavor": "gcc",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"arch": "x86_64",
"os": "none",
"disable-redzone": true,
"features": "-mmx,-sse,+soft-float"
}
```
As `llvm-target` we use `x86_64-unknown-none`, which defines the `x86_64` architecture, an `unknown` vendor, and no operating system (`none`). The ABI doesn't matter for us, so we just leave it off. The `data-layout` field is just copied from the `x86_64-unknown-linux-gnu` target. We also use the same values for the `target-endian`, `target-pointer-width`, `target-c-int-width`, and `arch` fields. For the `os` field we choose `none`, since our kernel runs on bare metal.
#### The Red Zone
The [red zone] is an optimization of the [System V ABI] that allows functions to temporary use the 128 bytes below its stack frame without adjusting the stack pointer:
[red zone]: https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
[System V ABI]: https://wiki.osdev.org/System_V_ABI
![stack frame with red zone](red-zone.svg)
The image shows the stack frame of a function with `n` local variables. On function entry, the stack pointer is adjusted to make room on the stack for the local variables.
The red zone is defined as the 128 bytes below the adjusted stack pointer. The function can use this area for temporary data that's not needed across function calls. Thus, the two instructions for adjusting the stack pointer can be avoided in some cases (e.g. in small leaf functions).
However, this optimization leads to huge problems with exceptions or hardware interrupts. Let's assume that an exception occurs while a function uses the red zone:
![red zone overwritten by exception handler](red-zone-overwrite.svg)
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. This might lead to strange bugs that [take weeks to debug].
[take weeks to debug]: https://forum.osdev.org/viewtopic.php?t=21720
To avoid such bugs when we implement exception handling in the future, we disable the red zone right from the beginning. This is achieved by adding the `"disable-redzone": true` line to our target configuration file.
#### SIMD Extensions
The `features` field enables/disables target features. We disable the `mmx` and `sse` features by prefixing them with a minus and enable the `soft-float` feature by prefixing it with a plus. The `mmx` and `sse` features determine support for [Single Instruction Multiple Data (SIMD)] instructions, which simultaneously perform an operation (e.g. addition) on multiple data words. The `x86` architecture supports the following standards:
[Single Instruction Multiple Data (SIMD)]: https://en.wikipedia.org/wiki/SIMD
- [MMX]: The _Multi Media Extension_ instruction set was introduced in 1997 and defines eight 64 bit registers called `mm0` through `mm7`. These registers are just aliases for the registers of the [x87 floating point unit].
- [SSE]: The _Streaming SIMD Extensions_ instruction set was introduced in 1999. Instead of re-using the floating point registers, it adds a completely new register set. The sixteen new registers are called `xmm0` through `xmm15` and are 128 bits each.
- [AVX]: The _Advanced Vector Extensions_ are extensions that further increase the size of the multimedia registers. The new registers are called `ymm0` through `ymm15` and are 256 bits each. They extend the `xmm` registers, so e.g. `xmm0` is the lower half of `ymm0`.
[MMX]: https://en.wikipedia.org/wiki/MMX_(instruction_set)
[x87 floating point unit]: https://en.wikipedia.org/wiki/X87
[SSE]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
[AVX]: https://en.wikipedia.org/wiki/Advanced_Vector_Extensions
By using such SIMD standards, programs can often speed up significantly. Good compilers are able to transform normal loops into such SIMD code automatically through a process called [auto-vectorization].
[auto-vectorization]: https://en.wikipedia.org/wiki/Automatic_vectorization
However, the large SIMD registers lead to problems in OS kernels. The reason is that the kernel has to backup all registers that it uses on each hardware interrupt (we will look into this in the [“Handling Exceptions”] post). So if the kernel uses SIMD registers, it has to backup a lot more data, which noticeably decreases performance. To avoid this performance loss, we disable the `sse` and `mmx` features (the `avx` feature is disabled by default).
As noted above, floating point operations on `x86_64` use SSE registers, so floats are no longer usable without SSE. Unfortunately, the Rust core library already uses floats (e.g., it implements traits for `f32` and `f64`), so we need an alternative way to implement float operations. The `soft-float` feature solves this problem by emulating all floating point operations through software functions based on normal integers.
### Compiling
To build our kernel for our new target, we pass the configuration file's name as `--target` argument. There is currently an [open bug][custom-target-bug] for custom target specifications, so you also need to set the `RUST_TARGET_PATH` environment variable to the current directory, otherwise Rust doesn't find your target. The full command is:
[custom-target-bug]: https://github.com/rust-lang/cargo/issues/4905
```
RUST_TARGET_PATH=$(pwd) cargo build --target x86_64-blog_os
```
However, the following error occurs:
```
error[E0463]: can't find crate for `core`
|
= note: the `x86_64-blog_os` target may not be installed
```
The error tells us that the Rust compiler no longer finds the core library. The [core library] is implicitly linked to all `no_std` crates and contains things such as `Result`, `Option`, and iterators.
[core library]: https://doc.rust-lang.org/nightly/core/index.html
The problem is that the core library is distributed together with the Rust compiler as a _precompiled_ library. So it is only valid for the host triple (e.g., `x86_64-unknown-linux-gnu`) but not for our custom target. If we want to compile code for other targets, we need to recompile `core` for these targets first.
#### Xargo
That's where [xargo] comes in. It is a wrapper for cargo that eases cross compilation. We can install it by executing:
[xargo]: https://github.com/japaric/xargo
```
cargo install xargo
```
Xargo depends on the rust source code, which we can install with `rustup component add rust-src`.
Xargo is “a drop-in replacement for cargo”, so every cargo command also works with `xargo`. You can do e.g. `xargo --help`, `xargo clean`, or `xargo doc`. However, the `build` command gains additional functionality: `xargo build` will automatically cross compile the `core` library when compiling for custom targets.
Let's try it:
```bash
> RUST_TARGET_PATH=$(pwd) xargo build --target=x86_64-blog_os
Compiling core v0.0.0 (file:///…/rust/src/libcore)
Finished release [optimized] target(s) in 22.87 secs
Compiling blog_os v0.1.0 (file:///…/blog_os/tags)
Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
```
It worked! We see that `xargo` cross-compiled the `core` library for our new custom target and then continued to compile our `blog_os` crate. After compilation, we can find a static library at `target/x86_64-blog_os/debug/libblog_os.a`, which can be linked with our assembly kernel.
## Integrating Rust
Let's try to integrate our Rust library into our assembly kernel so that we can call the `rust_main` function. For that we need to pass the `libblog_os.a` file to the linker, together with the assembly object files.
### Adjusting the Makefile
To build and link the rust library on `make`, we extend our `Makefile`([full file][github makefile]):
```make
# ...
target ?= $(arch)-blog_os
rust_os := target/$(target)/debug/libblog_os.a
# ...
.PHONY: all clean run iso kernel
# ...
$(kernel): kernel $(rust_os) $(assembly_object_files) $(linker_script)
@ld -n -T $(linker_script) -o $(kernel) \
$(assembly_object_files) $(rust_os)
kernel:
@RUST_TARGET_PATH=$(shell pwd) xargo build --target $(target)
```
We add a new `kernel` target that just executes `xargo build` and modify the `$(kernel)` target to link the created static lib. We also add the new `kernel` target to the `.PHONY` list, since it does not belong to a file with that name.
But now `xargo build` is executed on every `make`, even if no source file was changed. And the ISO is recreated on every `make iso`/`make run`, too. We could try to avoid this by adding dependencies on all rust source and cargo configuration files to the `kernel` target, but the ISO creation takes only half a second on my machine and most of the time we will have changed a Rust file when we run `make`. So we keep it simple for now and let cargo do the bookkeeping of changed files (it does it anyway).
[github makefile]: https://github.com/phil-opp/blog_os/blob/first_edition_post_3/Makefile
### Calling Rust
Now we can call the main method in `long_mode_start`:
```nasm
bits 64
long_mode_start:
...
; call the rust main
extern rust_main ; new
call rust_main ; new
; print `OKAY` to screen
mov rax, 0x2f592f412f4b2f4f
mov qword [0xb8000], rax
hlt
```
By defining `rust_main` as `extern` we tell nasm that the function is defined in another file. As the linker takes care of linking them together, we'll get a linker error if we have a typo in the name or forget to mark the rust function as `pub extern`.
If we've done everything right, we should still see the green `OKAY` when executing `make run`. That means that we successfully called the Rust function and returned back to assembly.
### Fixing Linker Errors
Now we can try some Rust code:
```rust
pub extern fn rust_main() {
let x = ["Hello", "World", "!"];
let y = x;
}
```
When we test it using `make run`, it fails with `undefined reference to 'memcpy'`. The `memcpy` function is one of the basic functions of the C library (`libc`). Usually the `libc` crate is linked to every Rust program together with the standard library, but we opted out through `#![no_std]`. We could try to fix this by adding the [libc crate] as `extern crate`. But `libc` is just a wrapper for the system `libc`, for example `glibc` on Linux, so this won't work for us. Instead we need to recreate the basic `libc` functions such as `memcpy`, `memmove`, `memset`, and `memcmp` in Rust.
[libc crate]: https://doc.rust-lang.org/1.10.0/libc/index.html
#### rlibc
Fortunately there already is a crate for that: [rlibc]. When we look at its [source code][rlibc source] we see that it contains no magic, just some [raw pointer] operations in a while loop. To add `rlibc` as a dependency we just need to add two lines to the `Cargo.toml`:
```toml
...
[dependencies]
rlibc = "1.0"
```
and an `extern crate` definition in our `src/lib.rs`:
```rust
...
extern crate rlibc;
#[no_mangle]
pub extern fn rust_main() {
...
```
Now `make run` doesn't complain about `memcpy` anymore. Instead it will show a pile of new ugly linker errors:
```
target/x86_64-blog_os/debug/libblog_os.a(core-92335f822fa6c9a6.0.o):
In function `_$LT$f32$u20$as$u20$core..num..dec2flt..
rawfp..RawFloat$GT$::from_int::h50f7952efac3fdca':
core.cgu-0.rs:(.text._ZN59_$LT$f32$u20$as$u20$core..num..dec2flt..
rawfp..RawFloat$GT$8from_int17h50f7952efac3fdcaE+0x2):
undefined reference to `__floatundisf'
target/x86_64-blog_os/debug/libblog_os.a(core-92335f822fa6c9a6.0.o):
In function `_$LT$f64$u20$as$u20$core..num..dec2flt..rawfp..
RawFloat$GT$::from_int::h12a81f175246914a':
core.cgu-0.rs:(.text._ZN59_$LT$f64$u20$as$u20$core..num..dec2flt..rawfp..
RawFloat$GT$8from_int17h12a81f175246914aE+0x2):
undefined reference to `__floatundidf'
target/x86_64-blog_os/debug/libblog_os.a(core-92335f822fa6c9a6.0.o):
In function `core::num::from_str_radix::h09b12650704e0508':
core.cgu-0.rs:(.text._ZN4core3num14from_str_radix
17h09b12650704e0508E+0xcf):
undefined reference to `__muloti4'
...
```
[rlibc]: https://crates.io/crates/rlibc
[rlibc source]: https://github.com/alexcrichton/rlibc/blob/defb486e765846417a8e73329e8c5196f1dca49a/src/lib.rs
[raw pointer]: https://doc.rust-lang.org/book/raw-pointers.html
[crates.io]: https://crates.io
#### --gc-sections
The new errors are linker errors about various missing functions such as `__floatundisf` or `__muloti4`. These functions are part of LLVM's [`compiler-rt` builtins] and are normally linked by the standard library. For `no_std` crates like ours, one has to link the `compiler-rt` library manually. Unfortunately, this library is implemented in C and the build process is a bit cumbersome. Alternatively, there is the [compiler-builtins] crate that tries to port the library to Rust, but it isn't complete yet.
[`compiler-rt` builtins]: https://compiler-rt.llvm.org/
[compiler-builtins]: https://github.com/rust-lang-nursery/compiler-builtins
In our case, there is a much simpler solution, since our kernel doesn't really need any of those functions yet. So we can just tell the linker to remove unused program sections and hopefully all references to these functions will disappear. Removing unused sections is generally a good idea as it reduces kernel size. The magic linker flag for this is `--gc-sections`, which stands for “garbage collect sections”. Let's add it to the `$(kernel)` target in our `Makefile`:
```make
$(kernel): xargo $(rust_os) $(assembly_object_files) $(linker_script)
@ld -n --gc-sections -T $(linker_script) -o $(kernel) \
$(assembly_object_files) $(rust_os)
```
Now we can do a `make run` again and it compiles without errors again. However, it doesn't boot anymore:
```
GRUB error: no multiboot header found.
```
What happened? Well, the linker removed unused sections. And since we don't use the Multiboot section anywhere, `ld` removes it, too. So we need to tell the linker explicitly that it should keep this section. The `KEEP` command does exactly that, so we add it to the linker script (`linker.ld`):
```
.boot :
{
/* ensure that the multiboot header is at the beginning */
KEEP(*(.multiboot_header))
}
```
Now everything should work again (the green `OKAY`). But there is another linking issue, which is triggered by some other example code.
#### panic = "abort"
The following snippet still fails:
```rust
...
let test = (0..3).flat_map(|x| 0..x).zip(0..);
```
The error is a linker error again (hence the ugly error message):
```
target/x86_64-blog_os/debug/libblog_os.a(blog_os-b5a29f28b14f1f1f.0.o):
In function `core::ptr::drop_in_place<core::iter::Zip<
core::iter::FlatMap<core::ops::Range<i32>, core::ops::Range<i32>,
closure>, core::ops::RangeFrom<i32>>>':
/…/rust/src/libcore/ptr.rs:66:
undefined reference to `_Unwind_Resume'
target/x86_64-blog_os/debug/libblog_os.a(blog_os-b5a29f28b14f1f1f.0.o):
In function `core::iter::iterator::Iterator::zip<core::iter::FlatMap<
core::ops::Range<i32>, core::ops::Range<i32>, closure>,
core::ops::RangeFrom<i32>>':
/…/rust/src/libcore/iter/iterator.rs:389:
undefined reference to `_Unwind_Resume'
...
```
So the linker can't find a function named `_Unwind_Resume` that is referenced e.g. in `iter/iterator.rs:389` in libcore. This reference is not really there at [line 389][iterator.rs:389] of libcore's `iterator.rs`. Instead, it is a compiler inserted _landing pad_, which is used for panic handling.
[iterator.rs:389]: https://github.com/rust-lang/rust/blob/c58c928e658d2e45f816fd05796a964aa83759da/src/libcore/iter/iterator.rs#L389
By default, the destructors of all stack variables are run when a `panic` occurs. This is called _unwinding_ and allows parent threads to recover from panics. However, it requires a platform specific gcc library, which isn't available in our kernel.
Fortunately, Rust allows us to disable unwinding for our target. For that we add the following line to our `x86_64-blog_os.json` file:
```json
{
"...",
"panic-strategy": "abort"
}
```
By setting the [panic strategy] to `abort` instead of the default `unwind`, we disable all unwinding in our kernel. Let's try `make run` again:
[panic strategy]: https://github.com/nox/rust-rfcs/blob/master/text/1513-less-unwinding.md
```
Compiling core v0.0.0 (file:///…/rust/src/libcore)
Finished release [optimized] target(s) in 22.24 secs
Finished dev [unoptimized + debuginfo] target(s) in 0.5 secs
target/x86_64-blog_os/debug/libblog_os.a(blog_os-b5a29f28b14f1f1f.0.o):
In function `core::ptr::drop_in_place<…>':
/…/src/libcore/ptr.rs:66:
undefined reference to `_Unwind_Resume'
...
```
We see that `xargo` recompiles the `core` crate, but the `_Unwind_Resume` error still occurs. This is because our `blog_os` crate was not recompiled somehow and thus still references the unwinding function. To fix this, we need to force a recompile using `cargo clean`:
```
> cargo clean
> make run
Compiling rlibc v1.0.0
Compiling blog_os v0.1.0 (file:///home/philipp/Documents/blog_os/tags)
warning: unused variable: `test` […]
Finished dev [unoptimized + debuginfo] target(s) in 0.60 secs
```
It worked! We no longer see linker errors and our kernel prints `OKAY` again.
## Hello World!
Finally, it's time for a `Hello World!` from Rust:
```rust
#[no_mangle]
pub extern fn rust_main() {
// ATTENTION: we have a very small stack and no guard page
let hello = b"Hello World!";
let color_byte = 0x1f; // white foreground, blue background
let mut hello_colored = [color_byte; 24];
for (i, char_byte) in hello.into_iter().enumerate() {
hello_colored[i*2] = *char_byte;
}
// write `Hello World!` to the center of the VGA text buffer
let buffer_ptr = (0xb8000 + 1988) as *mut _;
unsafe { *buffer_ptr = hello_colored };
loop{}
}
```
Some notes:
- The `b` prefix creates a [byte string], which is just an array of `u8`
- [enumerate] is an `Iterator` method that adds the current index `i` to elements
- `buffer_ptr` is a [raw pointer] that points to the center of the VGA text buffer
- Rust doesn't know the VGA buffer and thus can't guarantee that writing to the `buffer_ptr` is safe (it could point to important data). So we need to tell Rust that we know what we are doing by using an [unsafe block].
[byte string]: https://doc.rust-lang.org/reference/tokens.html#characters-and-strings
[enumerate]: https://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.enumerate
[unsafe block]: https://doc.rust-lang.org/book/unsafe.html
### Stack Overflows
Since we still use the small 64 byte [stack from the last post], we must be careful not to [overflow] it. Normally, Rust tries to avoid stack overflows through _guard pages_: The page below the stack isn't mapped and such a stack overflow triggers a page fault (instead of silently overwriting random memory). But we can't unmap the page below our stack right now since we currently use only a single big page. Fortunately the stack is located just above the page tables. So some important page table entry would probably get overwritten on stack overflow and then a page fault occurs, too.
[stack from the last post]: @/edition-1/posts/02-entering-longmode/index.md#creating-a-stack
[overflow]: https://en.wikipedia.org/wiki/Stack_overflow
## What's next?
Until now we write magic bits to some memory location when we want to print something to screen. In the [next post] we create a abstraction for the VGA text buffer that allows us to print strings in different colors and provides a simple interface.
[next post]: @/edition-1/posts/04-printing-to-screen/index.md

View File

@@ -1,92 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
<svg width="17cm" height="11cm" viewBox="-60 -21 340 212" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<text font-size="9.03111" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="90" y="65.9389">
<tspan x="90" y="65.9389"></tspan>
</text>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="204" y1="70" x2="176.236" y2="70"/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="185.118,65 175.118,70 185.118,75 "/>
</g>
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="208" y="72.75">
<tspan x="208" y="72.75">Old Stack Pointer</tspan>
</text>
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="75" y="230">
<tspan x="75" y="230"></tspan>
</text>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="70" x2="-4" y2="70"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="190" x2="-4" y2="190"/>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="-12" y1="72.2361" x2="-12" y2="187.764"/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-7,81.118 -12,71.118 -17,81.118 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-17,178.882 -12,188.882 -7,178.882 "/>
</g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="0" y1="-20" x2="0" y2="0"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="170" y1="-20" x2="170" y2="0"/>
<g>
<rect style="fill: #00ff00" x="0" y="0" width="170" height="20"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="0" width="170" height="20"/>
</g>
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="13.125">
<tspan x="85" y="13.125">Return Address</tspan>
</text>
<text font-size="9.03097" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="84" y="4">
<tspan x="84" y="4"></tspan>
</text>
<g>
<rect style="fill: #dddddd" x="0" y="20" width="170" height="20"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="20" width="170" height="20"/>
</g>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="33.125">
<tspan x="85" y="33.125">Local Variable 1</tspan>
</text>
<g>
<rect style="fill: #cccccc" x="0" y="40" width="170" height="32"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x="0" y="40" width="170" height="32"/>
</g>
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="59.125">
<tspan x="85" y="59.125">Local Variables 2..n</tspan>
</text>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="40" x2="170" y2="40"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="72" x2="120" y2="72"/>
<g>
<rect style="fill: #ff3333" x="0" y="70" width="170" height="120"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="70" width="170" height="120"/>
</g>
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="40" y="179.125">
<tspan x="40" y="179.125">Red Zone</tspan>
</text>
<text font-size="7.9021" style="fill: #000000;text-anchor:end;font-family:sans-serif;font-style:normal;font-weight:normal" x="-20" y="132.75">
<tspan x="-20" y="132.75">128 bytes</tspan>
</text>
<g>
<rect style="fill: #ffc200" x="30" y="70" width="140" height="30"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="70" width="140" height="30"/>
</g>
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="88.125">
<tspan x="100" y="88.125">Exception Stack Frame</tspan>
</text>
<g>
<rect style="fill: #ffe000" x="30" y="100" width="140" height="30"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="100" width="140" height="30"/>
</g>
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="118.125">
<tspan x="100" y="118.125">Register Backup</tspan>
</text>
<g>
<rect style="fill: #c6db97" x="30" y="130" width="140" height="30"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="130" width="140" height="30"/>
</g>
<text font-size="8.46654" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="147.925">
<tspan x="100" y="147.925">Handler Function Stack Frame</tspan>
</text>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #ff3333" x1="30" y1="70" x2="30" y2="160"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #ff3333" x1="30" y1="160" x2="170" y2="160"/>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="204" y1="160" x2="176.236" y2="160"/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="185.118,155 175.118,160 185.118,165 "/>
</g>
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="208" y="162.635">
<tspan x="208" y="162.635">New Stack Pointer</tspan>
</text>
</svg>

Before

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
<svg width="14cm" height="9cm" viewBox="-60 -21 270 172" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<text font-size="9.03111" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="90" y="65.9389">
<tspan x="90" y="65.9389"></tspan>
</text>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="154" y1="70" x2="126.236" y2="70"/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="135.118,65 125.118,70 135.118,75 "/>
</g>
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="158" y="72.75">
<tspan x="158" y="72.75">Stack Pointer</tspan>
</text>
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="75" y="230">
<tspan x="75" y="230"></tspan>
</text>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="70" x2="-4" y2="70"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="150" x2="-4" y2="150"/>
<g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="-12" y1="72.2361" x2="-12" y2="147.764"/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-7,81.118 -12,71.118 -17,81.118 "/>
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-17,138.882 -12,148.882 -7,138.882 "/>
</g>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="0" y1="-20" x2="0" y2="0"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="120" y1="-20" x2="120" y2="0"/>
<g>
<rect style="fill: #00ff00" x="0" y="0" width="120" height="20"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="0" width="120" height="20"/>
</g>
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="13.125">
<tspan x="60" y="13.125">Return Address</tspan>
</text>
<text font-size="9.03097" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="84" y="4">
<tspan x="84" y="4"></tspan>
</text>
<g>
<rect style="fill: #dddddd" x="0" y="20" width="120" height="20"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="20" width="120" height="20"/>
</g>
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="33.125">
<tspan x="60" y="33.125">Local Variable 1</tspan>
</text>
<g>
<rect style="fill: #cccccc" x="0" y="40" width="120" height="32"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x="0" y="40" width="120" height="32"/>
</g>
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="59.125">
<tspan x="60" y="59.125">Local Variables 2..n</tspan>
</text>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="40" x2="120" y2="40"/>
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="72" x2="120" y2="72"/>
<g>
<rect style="fill: #ff3333" x="0" y="70" width="120" height="80"/>
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="70" width="120" height="80"/>
</g>
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="113.125">
<tspan x="60" y="113.125">Red Zone</tspan>
</text>
<text font-size="7.9021" style="fill: #000000;text-anchor:end;font-family:sans-serif;font-style:normal;font-weight:normal" x="-20" y="112.75">
<tspan x="-20" y="112.75">128 bytes</tspan>
</text>
</svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -1,668 +0,0 @@
+++
title = "Printing to Screen"
weight = 4
path = "printing-to-screen"
aliases = ["printing-to-screen.html", "/2015/10/23/printing-to-screen/", "/rust-os/printing-to-screen.html"]
date = 2015-10-23
template = "edition-1/page.html"
[extra]
updated = "2016-10-31"
+++
In the [previous post] we switched from assembly to [Rust], a systems programming language that provides great safety. But so far we are using unsafe features like [raw pointers] whenever we want to print to screen. In this post we will create a Rust module that provides a safe and easy-to-use interface for the VGA text buffer. It will support Rust's [formatting macros], too.
[previous post]: @/edition-1/posts/03-set-up-rust/index.md
[Rust]: https://www.rust-lang.org/
[raw pointers]: https://doc.rust-lang.org/book/raw-pointers.html
[formatting macros]: https://doc.rust-lang.org/std/fmt/#related-macros
<!-- more -->
This post uses recent unstable features, so you need an up-to-date nighly compiler. If you have any questions, problems, or suggestions please [file an issue] or create a comment at the bottom. The code from this post is also available on [GitHub][code repository].
[file an issue]: https://github.com/phil-opp/blog_os/issues
[code repository]: https://github.com/phil-opp/blog_os/tree/first_edition_post_4
## The VGA Text Buffer
The text buffer starts at physical address `0xb8000` and contains the characters displayed on screen. It has 25 rows and 80 columns. Each screen character has the following format:
Bit(s) | Value
------ | ----------------
0-7 | ASCII code point
8-11 | Foreground color
12-14 | Background color
15 | Blink
The following colors are available:
Number | Color | Number + Bright Bit | Bright Color
------ | ---------- | ------------------- | -------------
0x0 | Black | 0x8 | Dark Gray
0x1 | Blue | 0x9 | Light Blue
0x2 | Green | 0xa | Light Green
0x3 | Cyan | 0xb | Light Cyan
0x4 | Red | 0xc | Light Red
0x5 | Magenta | 0xd | Pink
0x6 | Brown | 0xe | Yellow
0x7 | Light Gray | 0xf | White
Bit 4 is the _bright bit_, which turns for example blue into light blue. It is unavailable in background color as the bit is used to control if the text should blink. If you want to use a light background color (e.g. white) you have to disable blinking through a [BIOS function][disable blinking].
[disable blinking]: http://www.ctyme.com/intr/rb-0117.htm
## A basic Rust Module
Now that we know how the VGA buffer works, we can create a Rust module to handle printing:
```rust
//in src/lib.rs
mod vga_buffer;
```
The content of this module can live either in `src/vga_buffer.rs` or `src/vga_buffer/mod.rs`. The latter supports submodules while the former does not. But our module does not need any submodules so we create it as `src/vga_buffer.rs`.
All of the code below goes into our new module (unless specified otherwise).
### Colors
First, we represent the different colors using an enum:
```rust
#[allow(dead_code)]
#[repr(u8)]
pub enum Color {
Black = 0,
Blue = 1,
Green = 2,
Cyan = 3,
Red = 4,
Magenta = 5,
Brown = 6,
LightGray = 7,
DarkGray = 8,
LightBlue = 9,
LightGreen = 10,
LightCyan = 11,
LightRed = 12,
Pink = 13,
Yellow = 14,
White = 15,
}
```
We use a [C-like enum] here to explicitly specify the number for each color. Because of the `repr(u8)` attribute each enum variant is stored as an `u8`. Actually 4 bits would be sufficient, but Rust doesn't have an `u4` type.
[C-like enum]: https://doc.rust-lang.org/rust-by-example/custom_types/enum/c_like.html
Normally the compiler would issue a warning for each unused variant. By using the `#[allow(dead_code)]` attribute we disable these warnings for the `Color` enum.
To represent a full color code that specifies foreground and background color, we create a newtype on top of `u8`:
```rust
struct ColorCode(u8);
impl ColorCode {
const fn new(foreground: Color, background: Color) -> ColorCode {
ColorCode((background as u8) << 4 | (foreground as u8))
}
}
```
The `ColorCode` contains the full color byte, containing foreground and background color. Blinking is enabled implicitly by using a bright background color (soon we will disable blinking anyway). The `new` function is a [const function] to allow it in static initializers. As `const` functions are unstable we need to add the `const_fn` feature in `src/lib.rs`.
[const function]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
### The Text Buffer
Now we can add structures to represent a screen character and the text buffer:
```rust
#[repr(C)]
struct ScreenChar {
ascii_character: u8,
color_code: ColorCode,
}
const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;
struct Buffer {
chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT],
}
```
Since the field ordering in default structs is undefined in Rust, we need the [repr(C\)] attribute. It guarantees that the struct's fields are laid out exactly like in a C struct and thus guarantees the correct field ordering.
[repr(C\)]: https://doc.rust-lang.org/nightly/nomicon/other-reprs.html#reprc
To actually write to screen, we now create a writer type:
```rust
use core::ptr::Unique;
pub struct Writer {
column_position: usize,
color_code: ColorCode,
buffer: Unique<Buffer>,
}
```
The writer will always write to the last line and shift lines up when a line is full (or on `\n`). The `column_position` field keeps track of the current position in the last row. The current foreground and background colors are specified by `color_code` and a pointer to the VGA buffer is stored in `buffer`. To make it possible to create a `static` Writer later, the `buffer` field stores an `Unique<Buffer>` instead of a plain `*mut Buffer`. [Unique] is a wrapper that implements Send/Sync and is thus usable as a `static`. Since it's unstable, you may need to add the `unique` feature to `lib.rs`:
[Unique]: https://doc.rust-lang.org/1.10.0/core/ptr/struct.Unique.html
```rust
// in src/lib.rs
#![feature(unique)]
```
## Printing Characters
Now we can use the `Writer` to modify the buffer's characters. First we create a method to write a single ASCII byte (it doesn't compile yet):
```rust
impl Writer {
pub fn write_byte(&mut self, byte: u8) {
match byte {
b'\n' => self.new_line(),
byte => {
if self.column_position >= BUFFER_WIDTH {
self.new_line();
}
let row = BUFFER_HEIGHT - 1;
let col = self.column_position;
let color_code = self.color_code;
self.buffer().chars[row][col] = ScreenChar {
ascii_character: byte,
color_code: color_code,
};
self.column_position += 1;
}
}
}
fn buffer(&mut self) -> &mut Buffer {
unsafe{ self.buffer.as_mut() }
}
fn new_line(&mut self) {/* TODO */}
}
```
If the byte is the [newline] byte `\n`, the writer does not print anything. Instead it calls a `new_line` method, which we'll implement later. Other bytes get printed to the screen in the second match case.
[newline]: https://en.wikipedia.org/wiki/Newline
When printing a byte, the writer checks if the current line is full. In that case, a `new_line` call is required before to wrap the line. Then it writes a new `ScreenChar` to the buffer at the current position. Finally, the current column position is advanced.
The `buffer()` auxiliary method converts the raw pointer in the `buffer` field into a safe mutable buffer reference. The unsafe block is needed because the [as_mut()] method of `Unique` is unsafe. But our `buffer()` method itself isn't marked as unsafe, so it must not introduce any unsafety (e.g. cause segfaults). To guarantee that, it's very important that the `buffer` field always points to a valid `Buffer`. It's like a contract that we must stand to every time we create a `Writer`. To ensure that it's not possible to create an invalid `Writer` from outside of the module, the struct must have at least one private field and public creation functions are not allowed either.
[as_mut()]: https://doc.rust-lang.org/1.26.0/core/ptr/struct.Unique.html#method.as_mut
### Cannot Move out of Borrowed Content
When we try to compile it, we get the following error:
```
error[E0507]: cannot move out of borrowed content
--> src/vga_buffer.rs:79:34
|
79 | let color_code = self.color_code;
| ^^^^ cannot move out of borrowed content
```
The reason it that Rust _moves_ values by default instead of copying them like other languages. And we cannot move `color_code` out of `self` because we only borrowed `self`. For more information check out the [ownership section] in the Rust book.
[ownership section]: https://doc.rust-lang.org/book/ownership.html
[by reference]: https://rust-lang.github.io/book/ch04-02-references-and-borrowing.html
To fix it, we can implement the [Copy] trait for the `ColorCode` type. The easiest way to do this is to use the built-in [derive macro]:
[Copy]: https://doc.rust-lang.org/nightly/core/marker/trait.Copy.html
[derive macro]: https://doc.rust-lang.org/rust-by-example/custom_types/enum/c_like.html
```rust
#[derive(Debug, Clone, Copy)]
struct ColorCode(u8);
```
We also derive the [Clone] trait, since it's a requirement for `Copy`, and the [Debug] trait, which allows us to print this field for debugging purposes.
[Clone]: https://doc.rust-lang.org/nightly/core/clone/trait.Clone.html
[Debug]: https://doc.rust-lang.org/nightly/core/fmt/trait.Debug.html
Now our project should compile again.
However, the [documentation for Copy] says: _“if your type can implement Copy, it should”_. Therefore we also derive Copy for `Color` and `ScreenChar`:
[documentation for Copy]: https://doc.rust-lang.org/core/marker/trait.Copy.html#when-should-my-type-be-copy
```rust
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum Color {...}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct ScreenChar {...}
```
### Try it out!
To write some characters to the screen, you can create a temporary function:
```rust
pub fn print_something() {
let mut writer = Writer {
column_position: 0,
color_code: ColorCode::new(Color::LightGreen, Color::Black),
buffer: unsafe { Unique::new_unchecked(0xb8000 as *mut _) },
};
writer.write_byte(b'H');
}
```
It just creates a new Writer that points to the VGA buffer at `0xb8000`. To use the unstable `Unique::new_unchecked` function, we need to add the feature flag `#![feature(const_unique_new)]` to the top of our `src/lib.rs`.
Then it writes the byte `b'H'` to it. The `b` prefix creates a [byte character], which represents an ASCII code point. When we call `vga_buffer::print_something` in main, a `H` should be printed in the _lower_ left corner of the screen in light green:
[byte character]: https://doc.rust-lang.org/reference/tokens.html#characters-and-strings
![QEMU output with a green `H` in the lower left corner](vga-H-lower-left.png)
### Volatile
We just saw that our `H` was printed correctly. However, it might not work with future Rust compilers that optimize more aggressively.
The problem is that we only write to the `Buffer` and never read from it again. The compiler doesn't know about the side effect that some characters appear on the screen. So it might decide that these writes are unnecessary and can be omitted.
To avoid this erroneous optimization, we need to specify these writes as _[volatile]_. This tells the compiler that the write has side effects and should not be optimized away.
[volatile]: https://en.wikipedia.org/wiki/Volatile_(computer_programming)
In order to use volatile writes for the VGA buffer, we use the [volatile][volatile crate] library. This _crate_ (this is how packages are called in the Rust world) provides a `Volatile` wrapper type with `read` and `write` methods. These methods internally use the [read_volatile] and [write_volatile] functions of the standard library and thus guarantee that the reads/writes are not optimized away.
[volatile crate]: https://docs.rs/volatile
[read_volatile]: https://doc.rust-lang.org/nightly/core/ptr/fn.read_volatile.html
[write_volatile]: https://doc.rust-lang.org/nightly/core/ptr/fn.write_volatile.html
We can add a dependency on the `volatile` crate by adding it to the `dependencies` section of our `Cargo.toml`:
```toml
# in Cargo.toml
[dependencies]
volatile = "0.1.0"
```
The `0.1.0` is the [semantic] version number. For more information, see the [Specifying Dependencies] guide of the cargo documentation.
[semantic]: https://semver.org/
[Specifying Dependencies]: https://doc.crates.io/specifying-dependencies.html
Now we've declared that our project depends on the `volatile` crate and are able to import it in `src/lib.rs`:
```rust
// in src/lib.rs
extern crate volatile;
```
Let's use it to make writes to the VGA buffer volatile. We update our `Buffer` type as follows:
```rust
// in src/vga_buffer.rs
use volatile::Volatile;
struct Buffer {
chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}
```
Instead of a `ScreenChar`, we're now using a `Volatile<ScreenChar>`. (The `Volatile` type is [generic] and can wrap (almost) any type). This ensures that we can't accidentally write to it through a “normal” write. Instead, we have to use the `write` method now.
[generic]: https://doc.rust-lang.org/book/second-edition/ch10-00-generics.html
This means that we have to update our `Writer::write_byte` method:
```rust
impl Writer {
pub fn write_byte(&mut self, byte: u8) {
match byte {
b'\n' => self.new_line(),
byte => {
...
self.buffer().chars[row][col].write(ScreenChar {
ascii_character: byte,
color_code: color_code,
});
...
}
}
}
...
}
```
Instead of a normal assignment using `=`, we're now using the `write` method. This guarantees that the compiler will never optimize away this write.
## Printing Strings
To print whole strings, we can convert them to bytes and print them one-by-one:
```rust
// in `impl Writer`
pub fn write_str(&mut self, s: &str) {
for byte in s.bytes() {
self.write_byte(byte)
}
}
```
You can try it yourself in the `print_something` function.
When you print strings with some special characters like `ä` or `λ`, you'll notice that they cause weird symbols on screen. That's because they are represented by multiple bytes in [UTF-8]. By converting them to bytes, we of course get strange results. But since the VGA buffer doesn't support UTF-8, it's not possible to display these characters anyway.
[core tracking issue]: https://github.com/rust-lang/rust/issues/27701
[UTF-8]: https://www.fileformat.info/info/unicode/utf8.htm
### Support Formatting Macros
It would be nice to support Rust's formatting macros, too. That way, we can easily print different types like integers or floats. To support them, we need to implement the [core::fmt::Write] trait. The only required method of this trait is `write_str` that looks quite similar to our `write_str` method. To implement the trait, we just need to move it into an `impl fmt::Write for Writer` block and add a return type:
```rust
use core::fmt;
impl fmt::Write for Writer {
fn write_str(&mut self, s: &str) -> fmt::Result {
for byte in s.bytes() {
self.write_byte(byte)
}
Ok(())
}
}
```
The `Ok(())` is just a `Ok` Result containing the `()` type. We can drop the `pub` because trait methods are always public.
Now we can use Rust's built-in `write!`/`writeln!` formatting macros:
```rust
// in the `print_something` function
use core::fmt::Write;
let mut writer = Writer {...};
writer.write_byte(b'H');
writer.write_str("ello! ");
write!(writer, "The numbers are {} and {}", 42, 1.0/3.0);
```
Now you should see a `Hello! The numbers are 42 and 0.3333333333333333` at the bottom of the screen.
[core::fmt::Write]: https://doc.rust-lang.org/nightly/core/fmt/trait.Write.html
### Newlines
Right now, we just ignore newlines and characters that don't fit into the line anymore. Instead we want to move every character one line up (the top line gets deleted) and start at the beginning of the last line again. To do this, we add an implementation for the `new_line` method of `Writer`:
```rust
// in `impl Writer`
fn new_line(&mut self) {
for row in 1..BUFFER_HEIGHT {
for col in 0..BUFFER_WIDTH {
let buffer = self.buffer();
let character = buffer.chars[row][col].read();
buffer.chars[row - 1][col].write(character);
}
}
self.clear_row(BUFFER_HEIGHT-1);
self.column_position = 0;
}
fn clear_row(&mut self, row: usize) {/* TODO */}
```
We iterate over all screen characters and move each characters one row up. Note that the range notation (`..`) is exclusive the upper bound. We also omit the 0th row (the first range starts at `1`) because it's the row that is shifted off screen.
Now we only need to implement the `clear_row` method to finish the newline code:
```rust
// in `impl Writer`
fn clear_row(&mut self, row: usize) {
let blank = ScreenChar {
ascii_character: b' ',
color_code: self.color_code,
};
for col in 0..BUFFER_WIDTH {
self.buffer().chars[row][col].write(blank);
}
}
```
This method clears a row by overwriting all of its characters with a space character.
## Providing an Interface
To provide a global writer that can used as an interface from other modules, we can add a `static` writer:
```rust
pub static WRITER: Writer = Writer {
column_position: 0,
color_code: ColorCode::new(Color::LightGreen, Color::Black),
buffer: unsafe { Unique::new_unchecked(0xb8000 as *mut _) },
};
```
But we can't use it to print anything! You can try it yourself in the `print_something` function. The reason is that we try to take a mutable reference (`&mut`) to a immutable `static` when calling `WRITER.print_byte`.
To resolve it, we could use a [mutable static]. But then every read and write to it would be unsafe since it could easily introduce data races and other bad things. Using `static mut` is highly discouraged, there are even proposals to [remove it][remove static mut].
[mutable static]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
[remove static mut]: https://internals.rust-lang.org/t/pre-rfc-remove-static-mut/1437
But what are the alternatives? We could try to use a cell type like [RefCell] or even [UnsafeCell] to provide [interior mutability]. But these types aren't [Sync] \(with good reason), so we can't use them in statics.
[RefCell]: https://doc.rust-lang.org/nightly/core/cell/struct.RefCell.html
[UnsafeCell]: https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html
[interior mutability]: https://doc.rust-lang.org/1.30.0/book/first-edition/mutability.html#interior-vs-exterior-mutability
[Sync]: https://doc.rust-lang.org/nightly/core/marker/trait.Sync.html
To get synchronized interior mutability, users of the standard library can use [Mutex]. It provides mutual exclusion by blocking threads when the resource is already locked. But our basic kernel does not have any blocking support or even a concept of threads, so we can't use it either. However there is a really basic kind of mutex in computer science that requires no operating system features: the [spinlock]. Instead of blocking, the threads simply try to lock it again and again in a tight loop and thus burn CPU time until the mutex is free again.
[Mutex]: https://doc.rust-lang.org/nightly/std/sync/struct.Mutex.html
[spinlock]: https://en.wikipedia.org/wiki/Spinlock
To use a spinning mutex, we can add the [spin crate] as a dependency:
[spin crate]: https://crates.io/crates/spin
```toml
# in Cargo.toml
[dependencies]
rlibc = "0.1.4"
spin = "0.4.5"
```
```rust
// in src/lib.rs
extern crate spin;
```
Then we can use the spinning Mutex to add interior mutability to our static writer:
```rust
// in src/vga_buffer.rs again
use spin::Mutex;
...
pub static WRITER: Mutex<Writer> = Mutex::new(Writer {
column_position: 0,
color_code: ColorCode::new(Color::LightGreen, Color::Black),
buffer: unsafe { Unique::new_unchecked(0xb8000 as *mut _) },
});
```
[Mutex::new] is a const function, too, so it can be used in statics.
Now we can easily print from our main function:
[Mutex::new]: https://docs.rs/spin/0.4.5/spin/struct.Mutex.html#method.new
```rust
// in src/lib.rs
pub extern fn rust_main() {
use core::fmt::Write;
vga_buffer::WRITER.lock().write_str("Hello again");
write!(vga_buffer::WRITER.lock(), ", some numbers: {} {}", 42, 1.337);
loop{}
}
```
Note that we need to import the `Write` trait if we want to use its functions.
## A println macro
Rust's [macro syntax] is a bit strange, so we won't try to write a macro from scratch. Instead we look at the source of the [`println!` macro] in the standard library:
[macro syntax]: https://doc.rust-lang.org/nightly/book/second-edition/appendix-04-macros.html
[`println!` macro]: https://doc.rust-lang.org/nightly/std/macro.println!.html
```rust
macro_rules! println {
($fmt:expr) => (print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}
```
Macros are defined through one or more rules, which are similar to `match` arms. The `println` macro has two rules: The first rule is for invocations with a single argument (e.g. `println!("Hello")`) and the second rule is for invocations with additional parameters (e.g. `println!("{}{}", 4, 2)`).
Both rules simply append a newline character (`\n`) to the format string and then invoke the [`print!` macro], which is defined as:
[`print!` macro]: https://doc.rust-lang.org/nightly/std/macro.print!.html
```rust
macro_rules! print {
($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
}
```
The macro expands to a call of the [`_print` function] in the `io` module. The [`$crate` variable] ensures that the macro also works from outside the `std` crate. For example, it expands to `::std` when it's used in other crates.
The [`format_args` macro] builds a [fmt::Arguments] type from the passed arguments, which is passed to `_print`. The [`_print` function] of libstd is rather complicated, as it supports different `Stdout` devices. We don't need that complexity since we just want to print to the VGA buffer.
[`_print` function]: https://github.com/rust-lang/rust/blob/46d39f3329487115e7d7dcd37bc64eea6ef9ba4e/src/libstd/io/stdio.rs#L631
[`$crate` variable]: https://doc.rust-lang.org/1.30.0/book/first-edition/macros.html#the-variable-crate
[`format_args` macro]: https://doc.rust-lang.org/nightly/std/macro.format_args.html
[fmt::Arguments]: https://doc.rust-lang.org/nightly/core/fmt/struct.Arguments.html
To print to the VGA buffer, we just copy the `println!` macro and modify the `print!` macro to use our static `WRITER` instead of `_print`:
```rust
// in src/vga_buffer.rs
macro_rules! print {
($($arg:tt)*) => ({
use core::fmt::Write;
let mut writer = $crate::vga_buffer::WRITER.lock();
writer.write_fmt(format_args!($($arg)*)).unwrap();
});
}
```
Instead of a `_print` function, we call the `write_fmt` method of our static `Writer`. Since we're using a method from the `Write` trait, we need to import it before. The additional `unwrap()` at the end panics if printing isn't successful. But since we always return `Ok` in `write_str`, that should not happen.
Note the additional `{}` scope around the macro: We write `=> ({…})` instead of `=> (…)`. The additional `{}` avoids that the `Write` trait is silently imported to the parent scope when `print` is used.
### Clearing the screen
We can now use `println!` to add a rather trivial function to clear the screen:
```rust
// in src/vga_buffer.rs
pub fn clear_screen() {
for _ in 0..BUFFER_HEIGHT {
println!("");
}
}
```
### Hello World using `println`
To use `println` in `lib.rs`, we need to import the macros of the VGA buffer module first. Therefore we add a `#[macro_use]` attribute to the module declaration:
```rust
// in src/lib.rs
#[macro_use]
mod vga_buffer;
#[no_mangle]
pub extern fn rust_main() {
// ATTENTION: we have a very small stack and no guard page
vga_buffer::clear_screen();
println!("Hello World{}", "!");
loop{}
}
```
Since we imported the macros at crate level, they are available in all modules and thus provide an easy and safe interface to the VGA buffer.
As expected, we now see a _“Hello World!”_ on a cleared screen:
![QEMU printing “Hello World!” on a cleared screen](vga-hello-world.png)
### Deadlocks
Whenever we use locks, we must be careful to not accidentally introduce _deadlocks_. A [deadlock] occurs when a thread/program waits for a lock that will never be released. Normally, this happens when multiple threads access multiple locks. For example, when thread A holds lock 1 and tries to acquire lock 2 and -- at the same time -- thread B holds lock 2 and tries to acquire lock 1.
[deadlock]: https://en.wikipedia.org/wiki/Deadlock
However, a deadlock can also occur when a thread tries to acquire the same lock twice. This way we can trigger a deadlock in our VGA driver:
```rust
// in rust_main in src/lib.rs
println!("{}", { println!("inner"); "outer" });
```
The argument passed to `println` is new block that resolves to the string _“outer”_ (a block always returns the result of the last expression). But before returning “outer”, the block tries to print the string _“inner”_.
When we try this code in QEMU, we see that neither of the strings are printed. To understand what's happening, we take a look at our `print` macro again:
```rust
macro_rules! print {
($($arg:tt)*) => ({
use core::fmt::Write;
let mut writer = $crate::vga_buffer::WRITER.lock();
writer.write_fmt(format_args!($($arg)*)).unwrap();
});
}
```
So we _first_ lock the `WRITER` and then we evaluate the arguments using `format_args`. The problem is that the argument in our code example contains another `println`, which tries to lock the `WRITER` again. So now the inner `println` waits for the outer `println` and vice versa. Thus, a deadlock occurs and the CPU spins endlessly.
### Fixing the Deadlock
In order to fix the deadlock, we need to evaluate the arguments _before_ locking the `WRITER`. We can do so by moving the locking and printing logic into a new `print` function (like it's done in the standard library):
```rust
// in src/vga_buffer.rs
macro_rules! print {
($($arg:tt)*) => ({
$crate::vga_buffer::print(format_args!($($arg)*));
});
}
pub fn print(args: fmt::Arguments) {
use core::fmt::Write;
WRITER.lock().write_fmt(args).unwrap();
}
```
Now the macro only evaluates the arguments (through `format_args!`) and passes them to the new `print` function. The `print` function then locks the `WRITER` and prints the formatting arguments using `write_fmt`. So now the arguments are evaluated before locking the `WRITER`.
Thus, we fixed the deadlock:
![QEMU printing “inner” and then “outer”](fixed-println-deadlock.png)
We see that both “inner” and “outer” are printed.
## What's next?
In the next posts we will map the kernel pages correctly so that accessing `0x0` or writing to `.rodata` is not possible anymore. To obtain the loaded kernel sections we will read the Multiboot information structure. Then we will create a paging module and use it to switch to a new page table where the kernel sections are mapped correctly.
The [next post] describes the Multiboot information structure and creates a frame allocator using the information about memory areas.
[next post]: @/edition-1/posts/05-allocating-frames/index.md
## Other Rust OS Projects
Now that you know the very basics of OS development in Rust, you should also check out the following projects:
- [Rust Bare-Bones Kernel]: A basic kernel with roughly the same functionality as ours. Writes output to the serial port instead of the VGA buffer and maps the kernel to the [higher half] \(instead of our identity mapping).
_Note_: You need to [cross compile binutils] to build it (or you create some symbolic links[^fn-symlink] if you're on x86_64).
- [RustOS]: More advanced kernel that supports allocation, keyboard inputs, and threads. It also has a scheduler and a basic network driver.
- ["Tifflin" Experimental Kernel]: Big kernel project by thepowersgang, that is actively developed and has over 650 commits. It has a separate userspace and supports multiple file systems, even a GUI is included. Needs a cross compiler.
- [Redox]: Probably the most complete Rust OS today. It has an active community and over 1000 Github stars. File systems, network, an audio player, a picture viewer, and much more. Just take a look at the [screenshots][redox screenshots].
[Rust Bare-Bones Kernel]: https://github.com/thepowersgang/rust-barebones-kernel
[higher half]: https://wiki.osdev.org/Higher_Half_Kernel
[cross compile binutils]: @/edition-1/extra/cross-compile-binutils.md
[RustOS]: https://github.com/RustOS-Fork-Holding-Ground/RustOS
["Tifflin" Experimental Kernel]:https://github.com/thepowersgang/rust_os
[Redox]: https://github.com/redox-os/redox
[redox screenshots]: https://github.com/redox-os/redox#what-it-looks-like
## Footnotes
[^fn-symlink]: You will need to symlink `x86_64-none_elf-XXX` to `/usr/bin/XXX` where `XXX` is in {`as`, `ld`, `objcopy`, `objdump`, `strip`}. The `x86_64-none_elf-XXX` files must be in some folder that is in your `$PATH`. But then you can only build for your x86_64 host architecture, so use this hack only for testing.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -1,439 +0,0 @@
+++
title = "Allocating Frames"
weight = 5
path = "allocating-frames"
aliases = ["allocating-frames.html"]
date = 2015-11-15
template = "edition-1/page.html"
+++
In this post we create an allocator that provides free physical frames for a future paging module. To get the required information about available and used memory we use the Multiboot information structure. Additionally, we improve the `panic` handler to print the corresponding message and source line.
<!-- more -->
The full source code is available on [GitHub][source repo]. Feel free to open issues there if you have any problems or improvements. You can also leave a comment at the bottom.
[source repo]: https://github.com/phil-opp/blog_os/tree/first_edition_post_5
## Preparation
We still have a really tiny stack of 64 bytes, which won't suffice for this post. So we increase it to 16kB (four pages) in `boot.asm`:
```asm
section .bss
...
stack_bottom:
resb 4096 * 4
stack_top:
```
## The Multiboot Information Structure
When a Multiboot compliant bootloader loads a kernel, it passes a pointer to a boot information structure in the `ebx` register. We can use it to get information about available memory and loaded kernel sections.
First, we need to pass this pointer to our kernel as an argument to `rust_main`. To find out how arguments are passed to functions, we can look at the [calling convention of Linux]:
[calling convention of Linux]: https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI
> The first six integer or pointer arguments are passed in registers RDI, RSI, RDX, RCX, R8, and R9
So to pass the pointer to our kernel, we need to move it to `rdi` before calling the kernel. Since we're not using the `rdi`/`edi` register in our bootstrap code, we can simply set the `edi` register right after booting (in `boot.asm`):
```nasm
start:
mov esp, stack_top
mov edi, ebx ; Move Multiboot info pointer to edi
```
Now we can add the argument to our `rust_main`:
```rust
pub extern fn rust_main(multiboot_information_address: usize) { ... }
```
Instead of writing an own Multiboot module, we use the [multiboot2] crate. It gives us some basic information about mapped kernel sections and available memory. I just wrote it for this blog post since I could not find any other Multiboot 2 crate. It's still incomplete, but it does its job.
[multiboot2]: https://docs.rs/multiboot2
So let's add a dependency on the git repository:
```toml
# in Cargo.toml
[dependencies]
...
multiboot2 = "0.1.0"
```
```rust
// in src/lib.rs
extern crate multiboot2;
```
Now we can use it to print available memory areas.
### Available Memory
The boot information structure consists of various _tags_. See section 3.4 of the Multiboot specification ([PDF][multiboot specification]) for a complete list. The _memory map_ tag contains a list of all available RAM areas. Special areas such as the VGA text buffer at `0xb8000` are not available. Note that some of the available memory is already used by our kernel and by the multiboot information structure itself.
[multiboot specification]: https://nongnu.askapache.com/grub/phcoder/multiboot.pdf
To print all available memory areas, we can use the `multiboot2` crate in our `rust_main` as follows:
```rust
let boot_info = unsafe{ multiboot2::load(multiboot_information_address) };
let memory_map_tag = boot_info.memory_map_tag()
.expect("Memory map tag required");
println!("memory areas:");
for area in memory_map_tag.memory_areas() {
println!(" start: 0x{:x}, length: 0x{:x}",
area.base_addr, area.length);
}
```
The `load` function is `unsafe` because it relies on a valid address. Since the memory tag is not required by the Multiboot specification, the `memory_map_tag()` function returns an `Option`. The `memory_areas()` function returns the desired memory area iterator.
The output looks like this:
```
Hello World!
memory areas:
start: 0x0, length: 0x9fc00
start: 0x100000, length: 0x7ee0000
```
So we have one area from `0x0` to `0x9fc00`, which is a bit below the 1MiB mark. The second, bigger area starts at 1MiB and contains the rest of available memory. The area from `0x9fc00` to 1MiB is not available since it contains for example the VGA text buffer at `0xb8000`. This is the reason for putting our kernel at 1MiB and not somewhere below.
If you give QEMU more than 4GiB of memory by passing `-m 5G`, you get another unusable area below the 4GiB mark. This memory is normally mapped to some hardware devices. See the [OSDev Wiki][Memory_map] for more information.
[Memory_map]: https://wiki.osdev.org/Memory_Map_(x86)
### Handling Panics
We used `expect` in the code above, which will panic if there is no memory map tag. But our current panic handler just loops without printing any error message. Of course we could replace `expect` by a `match`, but we should fix the panic handler nonetheless:
```rust
#[lang = "panic_fmt"]
#[no_mangle]
pub extern fn panic_fmt() -> ! {
println!("PANIC");
loop{}
}
```
Now we get a `PANIC` message. But we can do even better. The `panic_fmt` function has actually some arguments:
```rust
#[lang = "panic_fmt"]
#[no_mangle]
pub extern fn panic_fmt(fmt: core::fmt::Arguments, file: &'static str,
line: u32) -> !
{
println!("\n\nPANIC in {} at line {}:", file, line);
println!(" {}", fmt);
loop{}
}
```
Be careful with these arguments as the compiler does not check the function signature for `lang_items`.
Now we get the panic message and the causing source line. You can try it by inserting a `panic` somewhere.
### Kernel ELF Sections
To read and print the sections of our kernel ELF file, we can use the _Elf-sections_ tag:
```rust
let elf_sections_tag = boot_info.elf_sections_tag()
.expect("Elf-sections tag required");
println!("kernel sections:");
for section in elf_sections_tag.sections() {
println!(" addr: 0x{:x}, size: 0x{:x}, flags: 0x{:x}",
section.addr, section.size, section.flags);
}
```
This should print out the start address and size of all kernel sections. If the section is writable, the `0x1` bit is set in `flags`. The `0x4` bit marks an executable section and the `0x2` bit indicates that the section was loaded in memory. For example, the `.text` section is executable but not writable and the `.data` section just the opposite.
But when we execute it, tons of really small sections are printed. We can use the `objdump -h build/kernel-x86_64.bin` command to list the sections with name. There seem to be over 200 sections and many of them start with `.text.*` or `.data.rel.ro.local.*`. This is because the Rust compiler puts e.g. each function in its own `.text` subsection. That way, unused functions are removed when the linker omits unused sections.
To merge these subsections, we need to update our linker script:
```
ENTRY(start)
SECTIONS {
. = 1M;
.boot :
{
KEEP(*(.multiboot_header))
}
.text :
{
*(.text .text.*)
}
.rodata : {
*(.rodata .rodata.*)
}
.data.rel.ro : {
*(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*)
}
}
```
These lines are taken from the default linker script of `ld`, which can be obtained through `ld verbose`. The `.text` _output_ section contains now all `.text.*` _input_ sections of the static library (and the same applies for the `.rodata` and `.data.rel.ro` sections).
Now there are only 12 sections left and we get a much more useful output:
![qemu output](qemu-memory-areas-and-kernel-sections.png)
If you like, you can compare this output to the `objdump -h build/kernel-x86_64.bin` output. You will see that the start addresses and sizes match exactly for each section. The sections with flags `0x0` are mostly debug sections, so they don't need to be loaded. And the last few sections of the QEMU output aren't in the `objdump` output because they are special sections such as string tables.
### Start and End of Kernel
We can now use the ELF section tag to calculate the start and end address of our loaded kernel:
```rust
let kernel_start = elf_sections_tag.sections().map(|s| s.addr)
.min().unwrap();
let kernel_end = elf_sections_tag.sections().map(|s| s.addr + s.size)
.max().unwrap();
```
The other used memory area is the Multiboot Information structure:
```rust
let multiboot_start = multiboot_information_address;
let multiboot_end = multiboot_start + (boot_info.total_size as usize);
```
Printing these numbers gives us:
```
kernel_start: 0x100000, kernel_end: 0x11a168
multiboot_start: 0x11d400, multiboot_end: 0x11d9c8
```
So the kernel starts at 1MiB (like expected) and is about 105 KiB in size. The multiboot information structure was placed at `0x11d400` by GRUB and needs 1480 bytes. Of course your numbers could be a bit different due to different versions of Rust or GRUB (or some differences in the source code).
## A frame allocator
When using paging, the physical memory is split into equally sized chunks (normally 4096 bytes) Such a chunk is called "physical page" or "frame". These frames can be mapped to any virtual page through page tables. For more information about paging take a peek at the [next post].
We will need a free frame in many cases. For example when want to increase the size of our future kernel heap. Or when we create a new page table. Or when we add a new kernel thread and thus need to allocate a new stack. So we need some kind of allocator that keeps track of physical frames and gives us a free one when needed.
There are various ways to write such a frame allocator:
We could create some kind of linked list from the free frames. For example, each frame could begin with a pointer to the next free frame. Since the frames are free, this would not overwrite any data. Our allocator would just save the head of the list and could easily allocate and deallocate frames by updating pointers. Unfortunately, this approach has a problem: It requires reading and writing these free frames. So we would need to map all physical frames to some virtual address, at least temporary. Another disadvantage is that we need to create this linked list at startup. That implies that we need to set over one million pointers at startup if the machine has 4GiB of RAM.
Another approach is to create some kind of data structure such as a [bitmap or a stack] to manage free frames. We could place it in the already identity mapped area right behind the kernel or multiboot structure. That way we would not need to (temporary) map each free frame. But it has the same problem of the slow initial creating/filling. In fact, we will use this approach in a future post to manage frames that are freed again. But for the initial management of free frames, we use a different method.
[bitmap or a stack]: https://wiki.osdev.org/Page_Frame_Allocation#Physical_Memory_Allocators
In the following, we will use Multiboot's memory map directly. The idea is to maintain a simple counter that starts at frame 0 and is increased constantly. If the current frame is available (part of an available area in the memory map) and not used by the kernel or the multiboot structure (we know their start and end addresses), we know that it's free and return it. Else, we increase the counter to the next possibly free frame. That way, we don't need to create a data structure when booting and the physical frames can remain unmapped. The only problem is that we cannot reasonably free frames again, but we will solve that problem in a future post (by adding an intermediate frame stack that saves freed frames).
<!--- TODO link future post -->
So let's start implementing our memory map based frame allocator.
### A Memory Module
First we create a memory module with a `Frame` type (`src/memory/mod.rs`):
```rust
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Frame {
number: usize,
}
```
(Don't forget to add the `mod memory` line to `src/lib.rs`.) Instead of e.g. the start address, we just store the frame number. We use `usize` here since the number of frames depends on the memory size. The long `derive` line makes frames printable and comparable.
To make it easy to get the corresponding frame for a physical address, we add a `containing_address` method:
```rust
pub const PAGE_SIZE: usize = 4096;
impl Frame {
fn containing_address(address: usize) -> Frame {
Frame{ number: address / PAGE_SIZE }
}
}
```
We also add a `FrameAllocator` trait:
```rust
pub trait FrameAllocator {
fn allocate_frame(&mut self) -> Option<Frame>;
fn deallocate_frame(&mut self, frame: Frame);
}
```
This allows us to create another, more advanced frame allocator in the future.
### The Allocator
Now we can put everything together and create the actual frame allocator. Therefor we create a `src/memory/area_frame_allocator.rs` submodule. The allocator struct looks like this:
```rust
use memory::{Frame, FrameAllocator};
use multiboot2::{MemoryAreaIter, MemoryArea};
pub struct AreaFrameAllocator {
next_free_frame: Frame,
current_area: Option<&'static MemoryArea>,
areas: MemoryAreaIter,
kernel_start: Frame,
kernel_end: Frame,
multiboot_start: Frame,
multiboot_end: Frame,
}
```
The `next_free_frame` field is a simple counter that is increased every time we return a frame. It's initialized to `0` and every frame below it counts as used. The `current_area` field holds the memory area that contains `next_free_frame`. If `next_free_frame` leaves this area, we will look for the next one in `areas`. When there are no areas left, all frames are used and `current_area` becomes `None`. The `{kernel, multiboot}_{start, end}` fields are used to avoid returning already used fields.
To implement the `FrameAllocator` trait, we need to implement the allocation and deallocation methods:
```rust
impl FrameAllocator for AreaFrameAllocator {
fn allocate_frame(&mut self) -> Option<Frame> {
// TODO (see below)
}
fn deallocate_frame(&mut self, frame: Frame) {
// TODO (see below)
}
}
```
The `allocate_frame` method looks like this:
```rust
// in `allocate_frame` in `impl FrameAllocator for AreaFrameAllocator`
if let Some(area) = self.current_area {
// "Clone" the frame to return it if it's free. Frame doesn't
// implement Clone, but we can construct an identical frame.
let frame = Frame{ number: self.next_free_frame.number };
// the last frame of the current area
let current_area_last_frame = {
let address = area.base_addr + area.length - 1;
Frame::containing_address(address as usize)
};
if frame > current_area_last_frame {
// all frames of current area are used, switch to next area
self.choose_next_area();
} else if frame >= self.kernel_start && frame <= self.kernel_end {
// `frame` is used by the kernel
self.next_free_frame = Frame {
number: self.kernel_end.number + 1
};
} else if frame >= self.multiboot_start && frame <= self.multiboot_end {
// `frame` is used by the multiboot information structure
self.next_free_frame = Frame {
number: self.multiboot_end.number + 1
};
} else {
// frame is unused, increment `next_free_frame` and return it
self.next_free_frame.number += 1;
return Some(frame);
}
// `frame` was not valid, try it again with the updated `next_free_frame`
self.allocate_frame()
} else {
None // no free frames left
}
```
The `choose_next_area` method isn't part of the trait and thus goes into a new `impl AreaFrameAllocator` block:
```rust
// in `impl AreaFrameAllocator`
fn choose_next_area(&mut self) {
self.current_area = self.areas.clone().filter(|area| {
let address = area.base_addr + area.length - 1;
Frame::containing_address(address as usize) >= self.next_free_frame
}).min_by_key(|area| area.base_addr);
if let Some(area) = self.current_area {
let start_frame = Frame::containing_address(area.base_addr as usize);
if self.next_free_frame < start_frame {
self.next_free_frame = start_frame;
}
}
}
```
This function chooses the area with the minimal base address that still has free frames, i.e. `next_free_frame` is smaller than its last frame. Note that we need to clone the iterator because the [min_by_key] function consumes it. If there are no areas with free frames left, `min_by_key` automatically returns the desired `None`.
[min_by_key]: https://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.min_by_key
If the `next_free_frame` is below the new `current_area`, it needs to be updated to the area's start frame. Else, the `allocate_frame` call could return an unavailable frame.
We don't have a data structure to store free frames, so we can't implement `deallocate_frame` reasonably. Thus we use the `unimplemented` macro, which just panics when the method is called:
```rust
impl FrameAllocator for AreaFrameAllocator {
fn allocate_frame(&mut self) -> Option<Frame> {
// described above
}
fn deallocate_frame(&mut self, _frame: Frame) {
unimplemented!()
}
}
```
Now we only need a constructor function to make the allocator usable:
```rust
pub fn new(kernel_start: usize, kernel_end: usize,
multiboot_start: usize, multiboot_end: usize,
memory_areas: MemoryAreaIter) -> AreaFrameAllocator
{
let mut allocator = AreaFrameAllocator {
next_free_frame: Frame::containing_address(0),
current_area: None,
areas: memory_areas,
kernel_start: Frame::containing_address(kernel_start),
kernel_end: Frame::containing_address(kernel_end),
multiboot_start: Frame::containing_address(multiboot_start),
multiboot_end: Frame::containing_address(multiboot_end),
};
allocator.choose_next_area();
allocator
}
```
Note that we call `choose_next_area` manually here because `allocate_frame` returns `None` as soon as `current_area` is `None`. So by calling `choose_next_area` we initialize it to the area with the minimal base address.
### Testing it
In order to test it in main, we need to [re-export] the `AreaFrameAllocator` in the `memory` module. Then we can create a new allocator:
[re-export]: https://doc.rust-lang.org/1.30.0/book/first-edition/crates-and-modules.html#re-exporting-with-pub-use
```rust
let mut frame_allocator = memory::AreaFrameAllocator::new(
kernel_start as usize, kernel_end as usize, multiboot_start,
multiboot_end, memory_map_tag.memory_areas());
```
Now we can test it by adding some frame allocations:
```rust
println!("{:?}", frame_allocator.allocate_frame());
```
You will see that the frame number starts at `0` and increases steadily, but the kernel and Multiboot frames are left out (you need to allocate many frames to see this since the kernel starts at frame 256).
The following `for` loop allocates all frames and prints out the total number of allocated frames:
```rust
for i in 0.. {
if let None = frame_allocator.allocate_frame() {
println!("allocated {} frames", i);
break;
}
}
```
You can try different amounts of memory by passing e.g. `-m 500M` to QEMU. To compare these numbers, [WolframAlpha] can be very helpful.
[WolframAlpha]: https://www.wolframalpha.com/input/?i=%2832605+*+4096%29+bytes+in+MiB
## Conclusion
Now we have a working frame allocator. It is a bit rudimentary and cannot free frames, but it also is very fast since it reuses the Multiboot memory map and does not need any costly initialization. A future post will build upon this allocator and add a stack-like data structure for freed frames.
## What's next?
The [next post] will be about paging again. We will use the frame allocator to create a safe module that allows us to switch page tables and map pages. Then we will use this module and the information from the Elf-sections tag to remap the kernel correctly.
[next post]: @/edition-1/posts/06-page-tables/index.md
## Recommended Posts
Eric Kidd started the [Bare Metal Rust] series last week. Like this post, it builds upon the code from [Printing to Screen], but tries to support keyboard input instead of wrestling through memory management details.
[Bare Metal Rust]: http://www.randomhacks.net/bare-metal-rust/
[Printing to Screen]: @/edition-1/posts/04-printing-to-screen/index.md

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,929 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="7.2973523in"
height="4.1279249in"
viewBox="-2141 2141 8777.1352 4929.2808"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="X86_Paging_64bit.svg">
<metadata
id="metadata370">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs368" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1080"
inkscape:window-height="1868"
id="namedview366"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.4404005"
inkscape:cx="383.13253"
inkscape:cy="73.778068"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g4" />
<g
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="g4"
transform="translate(-2845,-286.64442)">
<rect
x="8503"
y="4015"
width="708"
height="3307"
rx="0"
style="fill:#dfdfdf"
id="rect8" />
<polyline
points="3365,2598 3365,3070"
id="polyline10" />
<polyline
points="3189,2598 3189,3070"
id="polyline12" />
<polyline
points="3012,2598 3012,3070"
id="polyline14" />
<polyline
points="2834,2598 2834,3070"
id="polyline16" />
<polyline
points="2657,2598 2657,3070"
id="polyline18" />
<polyline
points="2480,2598 2480,3070"
id="polyline20" />
<polyline
points="2303,2598 2303,3070"
id="polyline22" />
<polyline
points="2125,2456 2125,3070"
id="polyline24" />
<polyline
points="1948,2598 1948,3070"
id="polyline26" />
<polyline
points="1772,2598 1772,3070"
id="polyline28" />
<polyline
points="1594,2598 1594,3070"
id="polyline30" />
<polyline
points="1417,2598 1417,3070"
id="polyline32" />
<polyline
points="1239,2598 1239,3070"
id="polyline34" />
<polyline
points="1063,2598 1063,3070"
id="polyline36" />
<polyline
points="886,2598 886,3070"
id="polyline38" />
<polyline
points="708,2456 708,3070"
id="polyline40" />
<polyline
points="3543,2456 3543,3070"
id="polyline74" />
<polyline
points="-2125,2598 3543,2598"
id="polyline76"
transform="matrix(0.49978943,0,0,1,1772.2461,0)" />
<polyline
points="3543,3070 -1948,3070 -2125,3070"
id="polyline78"
transform="matrix(0.49786923,0,0,1,1779.0493,0)" />
<polyline
points="9035,2598 9035,3070"
id="polyline80" />
<polyline
points="8858,2598 8858,3070"
id="polyline82" />
<polyline
points="8681,2598 8681,3070"
id="polyline84" />
<polyline
points="8503,2598 8503,3070"
id="polyline86" />
<polyline
points="8326,2598 8326,3070"
id="polyline88" />
<polyline
points="8150,2598 8150,3070"
id="polyline90" />
<polyline
points="7972,2598 7972,3070"
id="polyline92" />
<polyline
points="7795,2456 7795,3070"
id="polyline94" />
<polyline
points="7617,2598 7617,3070"
id="polyline96" />
<polyline
points="7441,2598 7441,3070"
id="polyline98" />
<polyline
points="7264,2598 7264,3070"
id="polyline100" />
<polyline
points="7086,2598 7086,3070"
id="polyline102" />
<polyline
points="6909,2598 6909,3070"
id="polyline104" />
<polyline
points="6732,2598 6732,3070"
id="polyline106" />
<polyline
points="6555,2598 6555,3070"
id="polyline108" />
<polyline
points="6377,2456 6377,3070"
id="polyline110" />
<polyline
points="6200,2598 6200,3070"
id="polyline112" />
<polyline
points="6024,2598 6024,3070"
id="polyline114" />
<polyline
points="5846,2598 5846,3070"
id="polyline116" />
<polyline
points="5669,2598 5669,3070"
id="polyline118" />
<polyline
points="5491,2598 5491,3070"
id="polyline120" />
<polyline
points="5315,2598 5315,3070"
id="polyline122" />
<polyline
points="5138,2598 5138,3070"
id="polyline124" />
<polyline
points="4960,2409 4960,3070"
id="polyline126" />
<polyline
points="4783,2598 4783,3070"
id="polyline128" />
<polyline
points="4606,2598 4606,3070"
id="polyline130" />
<polyline
points="4429,2598 4429,3070"
id="polyline132" />
<polyline
points="4251,2598 4251,3070"
id="polyline134" />
<polyline
points="4074,2598 4074,3070"
id="polyline136" />
<polyline
points="3898,2598 3898,3070"
id="polyline138" />
<polyline
points="3720,2598 3720,3070"
id="polyline140" />
<polyline
points="9212,2456 9212,3070"
id="polyline142" />
<polyline
points="3543,2598 9212,2598"
id="polyline144" />
<polyline
points="9212,3070 3720,3070 0,3070"
id="polyline146"
transform="matrix(0.92335242,0,0,1,706.07747,0)" />
<rect
x="5102"
y="4488"
width="1181"
height="2362"
rx="0"
style="fill:#dfdfdf"
id="rect148" />
<rect
x="3307"
y="4251"
width="1181"
height="2362"
rx="0"
style="fill:#dfdfdf"
id="rect150" />
<rect
x="6897"
y="4724"
width="1181"
height="2362"
rx="0"
style="fill:#dfdfdf"
id="rect152" />
<rect
x="1511"
y="4015"
width="1181"
height="2362"
rx="0"
style="fill:#dfdfdf"
id="rect154" />
<rect
x="1417"
y="6850"
width="1417"
height="472"
rx="0"
id="rect156" />
<rect
x="8503"
y="5433"
width="708"
height="236"
rx="0"
style="fill:#dfdfdf"
id="rect158" />
<rect
x="5102"
y="5669"
width="1181"
height="472"
rx="0"
id="rect160" />
<rect
x="3307"
y="5433"
width="1181"
height="472"
rx="0"
id="rect162" />
<rect
x="6897"
y="5905"
width="1181"
height="472"
rx="0"
id="rect164" />
<rect
x="1511"
y="5196"
width="1181"
height="472"
rx="0"
id="rect166" />
<circle
cx="6141"
cy="5905"
r="47"
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
id="circle170" />
<circle
cx="4346"
cy="5669"
r="47"
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
id="circle172" />
<circle
cx="7937"
cy="6141"
r="47"
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
id="circle174" />
<circle
cx="2551"
cy="5433"
r="47"
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
id="circle176" />
<polyline
points="3118,3590 2834,3779"
style="stroke:#0000ff"
id="polyline178" />
<polyline
points="1322,3590 1039,3779"
style="stroke:#0000ff"
id="polyline180" />
<polyline
points="4913,3590 4629,3779"
style="stroke:#0000ff"
id="polyline184" />
<polyline
points="6519,3590 6236,3779"
style="stroke:#0000ff"
id="polyline186" />
<polyline
points="8314,3590 8031,3779"
style="stroke:#0000ff"
id="polyline188" />
<circle
cx="1653"
cy="7086"
r="47"
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
id="circle190" />
<polyline
points="1653,7086 1181,7086 1181,6377 1354,6377"
style="stroke:#0000ff"
id="polyline192" />
<polygon
points="1512,6377 1355,6330 1355,6425 1355,6425 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon194" />
<polyline
points="7937,6141 8173,6141 8173,7322 8346,7322"
style="stroke:#0000ff"
id="polyline196" />
<polygon
points="8504,7322 8347,7275 8347,7370 8347,7370 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon198" />
<polyline
points="8173,3543 8173,5669 8346,5669"
style="stroke:#0000ff"
id="polyline200" />
<polygon
points="8504,5669 8347,5622 8347,5716 8347,5716 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon202" />
<polyline
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
style="stroke:#0000ff"
id="polyline204" />
<polyline
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
style="stroke:#0000ff"
id="polyline206" />
<polyline
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
style="stroke:#0000ff"
id="polyline208" />
<polyline
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
style="stroke:#0000ff"
id="polyline210" />
<polyline
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
style="stroke:#0000ff"
id="polyline212" />
<polyline
points="4771,3543 4771,6141 4944,6141"
style="stroke:#0000ff"
id="polyline214" />
<polygon
points="5103,6141 4945,6094 4945,6188 4945,6188 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon216" />
<polyline
points="6377,3543 6377,4015 6519,4157 6519,6377 6740,6377"
style="stroke:#0000ff"
id="polyline218" />
<polygon
points="6898,6377 6741,6330 6741,6425 6741,6425 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon220" />
<polyline
points="6141,5905 6377,5905 6377,7086 6740,7086"
style="stroke:#0000ff"
id="polyline222" />
<polygon
points="6898,7086 6741,7039 6741,7133 6741,7133 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon224" />
<polyline
points="4346,5669 4629,5669 4629,6850 4944,6850"
style="stroke:#0000ff"
id="polyline226" />
<polygon
points="5103,6850 4945,6803 4945,6897 4945,6897 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon228" />
<polyline
points="2598,5433 2834,5433 2834,6614 3149,6614"
style="stroke:#0000ff"
id="polyline230" />
<polygon
points="3308,6614 3150,6566 3150,6661 3150,6661 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon232" />
<polyline
points="1181,3543 1181,5669 1401,5669"
style="stroke:#0000ff"
id="polyline234" />
<polygon
points="1560,5669 1402,5622 1402,5716 1402,5716 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon236" />
<polyline
points="2976,3543 2976,5905 3149,5905"
style="stroke:#0000ff"
id="polyline238" />
<polygon
points="3308,5905 3150,5858 3150,5952 3150,5952 "
style="fill:#0000ff;stroke:#0000ff"
id="polygon240" />
<g
style="fill:#000000;stroke-width:0"
id="g242">
<text
xml:space="preserve"
x="2281.8145"
y="7165"
font-style="normal"
font-weight="normal"
font-size="152"
id="text244"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">CR3 register</text>
<text
xml:space="preserve"
x="3507"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text246"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
<text
xml:space="preserve"
x="2161"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text248"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
<text
xml:space="preserve"
x="2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text250"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
<text
xml:space="preserve"
x="744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text252"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
<text
xml:space="preserve"
x="672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text254"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text256"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="-744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text258"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text260"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="9176"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text262"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
<text
xml:space="preserve"
x="7759"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text264"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
<text
xml:space="preserve"
x="6342"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text266"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
<text
xml:space="preserve"
x="4924"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text268"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
<text
xml:space="preserve"
x="3579"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text270"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
<text
xml:space="preserve"
x="6413"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text272"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
<text
xml:space="preserve"
x="7830"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text274"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
<text
xml:space="preserve"
x="4996"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text276"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
<g
transform="matrix(0,-1,1,0,8976,4724)"
id="g278">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text280"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<g
transform="matrix(0,-1,1,0,8976,6614)"
id="g282">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text284"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<g
transform="matrix(0,-1,1,0,9448,5622)"
id="g286">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="152"
id="text288"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46390561px"
id="tspan3509">4K memory page</tspan></text>
</g>
<text
xml:space="preserve"
x="-2125"
y="2314"
font-style="normal"
font-weight="normal"
font-size="152"
id="text290"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<text
xml:space="preserve"
x="5622"
y="5958.457"
font-style="normal"
font-weight="normal"
font-size="152"
id="text292"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46391296px"
id="tspan3497">P2 entry</tspan></text>
<g
transform="matrix(0,-1,1,0,5723,6614)"
id="g296">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text298"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<g
transform="matrix(0,-1,1,0,5723,5196)"
id="g300">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text302"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="5692"
y="4393"
font-style="normal"
font-weight="normal"
font-size="152"
id="text304"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46390561px"
id="tspan3505">P2 table</tspan></text>
<g
transform="matrix(0,-1,1,0,3928,6377)"
id="g306">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<g
transform="matrix(0,-1,1,0,3928,4960)"
id="g310">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text312"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="3826"
y="5722.457"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46391296px"
id="tspan3495">P3 entry</tspan></text>
<text
xml:space="preserve"
x="3874"
y="3968"
font-style="normal"
font-weight="normal"
font-size="152"
id="text318"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
xml:space="preserve"
x="3874"
y="4157"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46390561px"
id="tspan3503">P3 table</tspan></text>
<text
xml:space="preserve"
x="7417"
y="6194.457"
font-style="normal"
font-weight="normal"
font-size="152"
id="text322"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46391296px"
id="tspan3499">P1 entry </tspan></text>
<g
transform="matrix(0,-1,1,0,7519,6850)"
id="g326">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text328"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<g
transform="matrix(0,-1,1,0,7519,5433)"
id="g330">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text332"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="7487"
y="4629"
font-style="normal"
font-weight="normal"
font-size="152"
id="text334"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46390561px"
id="tspan3507">P1 table</tspan></text>
<g
transform="matrix(0,-1,1,0,2133,6141)"
id="g336">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text338"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<g
transform="matrix(0,-1,1,0,2133,4724)"
id="g340">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text342"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
x="2004.2715"
y="5486.457"
font-style="normal"
font-weight="normal"
font-size="152"
id="text344"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">
<tspan
style="font-size:200.46390561px"
id="tspan3493">P4 entry</tspan>
</text>
<text
x="2031"
y="5622"
font-style="normal"
font-weight="normal"
font-size="152"
id="text346"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
x="2078"
y="3941.2715"
font-style="normal"
font-weight="normal"
font-size="152"
id="text348"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">
<tspan
style="font-size:200.46391296px"
id="tspan3501">P4 table</tspan>
</text>
<text
x="3165"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text350"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
<text
x="1370"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text352"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
<text
x="1370"
y="6708"
font-style="normal"
font-weight="normal"
font-size="152"
id="text354"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
<text
x="4960"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text356"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
<text
x="6566"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text358"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
<text
x="8362"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text360"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
<text
x="1181"
y="7532.2715"
font-style="normal"
font-weight="normal"
font-size="152"
id="text364"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -1,893 +0,0 @@
+++
title = "Page Tables"
weight = 6
path = "page-tables"
aliases = ["page-tables.html", "modifying-page-tables.html"]
date = 2015-12-09
template = "edition-1/page.html"
+++
In this post we will create a paging module, which allows us to access and modify the 4-level page table. We will explore recursive page table mapping and use some Rust features to make it safe. Finally we will create functions to translate virtual addresses and to map and unmap pages.
<!-- more -->
You can find the source code and this post itself on [GitHub][source repository]. Please file an issue there if you have any problems or improvement suggestions. There is also a comment section at the end of this page. Note that this post requires a current Rust nightly.
[source repository]: https://github.com/phil-opp/blog_os/tree/first_edition_post_6
## Paging
_Paging_ is a memory management scheme that separates virtual and physical memory. The address space is split into equal sized _pages_ and _page tables_ specify which virtual page points to which physical frame. For an extensive paging introduction take a look at the paging chapter ([PDF][paging chapter]) of the [Three Easy Pieces] OS book.
[paging chapter]: http://pages.cs.wisc.edu/~remzi/OSTEP/vm-paging.pdf
[Three Easy Pieces]: http://pages.cs.wisc.edu/~remzi/OSTEP/
The x86 architecture uses a 4-level page table in 64-bit mode. A virtual address has the following structure:
![structure of a virtual address on x86](x86_address_structure.svg)
The bits 4863 are so-called _sign extension_ bits and must be copies of bit 47. The following 36 bits define the page table indexes (9 bits per table) and the last 12 bits specify the offset in the 4KiB page.
Each table has 2^9 = 512 entries and each entry is 8 byte. Thus a page table fits exactly in one page (4 KiB).
To translate an address, the CPU reads the P4 address from the CR3 register. Then it uses the indexes to walk the tables:
![translation of virtual to physical addresses in 64 bit mode](X86_Paging_64bit.svg)
The P4 entry points to a P3 table, where the next 9 bits of the address are used to select an entry. The P3 entry then points to a P2 table and the P2 entry points to a P1 table. The P1 entry, which is specified through bits 1220, finally points to the physical frame.
## A Basic Paging Module
Let's create a basic paging module in `memory/paging/mod.rs`:
```rust
use memory::PAGE_SIZE; // needed later
const ENTRY_COUNT: usize = 512;
pub type PhysicalAddress = usize;
pub type VirtualAddress = usize;
pub struct Page {
number: usize,
}
```
We import the `PAGE_SIZE` and define a constant for the number of entries per table. To make future function signatures more expressive, we can use the type aliases `PhysicalAddress` and `VirtualAddress`. The `Page` struct is similar to the `Frame` struct in the [previous post], but represents a virtual page instead of a physical frame.
[previous post]: @/edition-1/posts/05-allocating-frames/index.md#a-memory-module
### Page Table Entries
To model page table entries, we create a new `entry` submodule:
```rust
use memory::Frame; // needed later
pub struct Entry(u64);
impl Entry {
pub fn is_unused(&self) -> bool {
self.0 == 0
}
pub fn set_unused(&mut self) {
self.0 = 0;
}
}
```
We define that an unused entry is completely 0. That allows us to distinguish unused entries from other non-present entries in the future. For example, we could define one of the available bits as the `swapped_out` bit for pages that are swapped to disk.
Next we will model the contained physical address and the various flags. Remember, entries have the following format:
Bit(s) | Name | Meaning
--------------------- | ------ | ----------------------------------
0 | present | the page is currently in memory
1 | writable | it's allowed to write to this page
2 | user accessible | if not set, only kernel mode code can access this page
3 | write through caching | writes go directly to memory
4 | disable cache | no cache is used for this page
5 | accessed | the CPU sets this bit when this page is used
6 | dirty | the CPU sets this bit when a write to this page occurs
7 | huge page/null | must be 0 in P1 and P4, creates a 1GiB page in P3, creates a 2MiB page in P2
8 | global | page isn't flushed from caches on address space switch (PGE bit of CR4 register must be set)
9-11 | available | can be used freely by the OS
12-51 | physical address | the page aligned 52bit physical address of the frame or the next page table
52-62 | available | can be used freely by the OS
63 | no execute | forbid executing code on this page (the NXE bit in the EFER register must be set)
To model the various flags, we will use the [bitflags] crate. To add it as a dependency, add the following to your `Cargo.toml`:
[bitflags]: https://github.com/rust-lang-nursery/bitflags
```toml
[dependencies]
...
bitflags = "0.9.1"
```
To import the macro, we need to use `#[macro_use]` above the `extern crate` definition:
```rust
// in src/lib.rs
#[macro_use]
extern crate bitflags;
```
Now we can model the various flags:
```rust
bitflags! {
pub struct EntryFlags: u64 {
const PRESENT = 1 << 0;
const WRITABLE = 1 << 1;
const USER_ACCESSIBLE = 1 << 2;
const WRITE_THROUGH = 1 << 3;
const NO_CACHE = 1 << 4;
const ACCESSED = 1 << 5;
const DIRTY = 1 << 6;
const HUGE_PAGE = 1 << 7;
const GLOBAL = 1 << 8;
const NO_EXECUTE = 1 << 63;
}
}
```
To extract the flags from the entry we create an `Entry::flags` method that uses [from_bits_truncate]:
[from_bits_truncate]: https://docs.rs/bitflags/0.9.1/bitflags/example_generated/struct.Flags.html#method.from_bits_truncate
```rust
pub fn flags(&self) -> EntryFlags {
EntryFlags::from_bits_truncate(self.0)
}
```
This allows us to check for flags through the `contains()` function. For example, `flags().contains(PRESENT | WRITABLE)` returns true if the entry contains _both_ flags.
To extract the physical address, we add a `pointed_frame` method:
```rust
pub fn pointed_frame(&self) -> Option<Frame> {
if self.flags().contains(PRESENT) {
Some(Frame::containing_address(
self.0 as usize & 0x000fffff_fffff000
))
} else {
None
}
}
```
If the entry is present, we mask bits 1251 and return the corresponding frame. If the entry is not present, it does not point to a valid frame so we return `None`.
To modify entries, we add a `set` method that updates the flags and the pointed frame:
```rust
pub fn set(&mut self, frame: Frame, flags: EntryFlags) {
assert!(frame.start_address() & !0x000fffff_fffff000 == 0);
self.0 = (frame.start_address() as u64) | flags.bits();
}
```
The start address of a frame should be page aligned and smaller than 2^52 (since x86 uses 52bit physical addresses). Since an invalid address could mess up the entry, we add an assertion. To actually set the entry, we just need to `or` the start address and the flag bits.
The missing `Frame::start_address` method is pretty simple:
```rust
use self::paging::PhysicalAddress;
fn start_address(&self) -> PhysicalAddress {
self.number * PAGE_SIZE
}
```
We add it to the `impl Frame` block in `memory/mod.rs`.
### Page Tables
To model page tables, we create a basic `Table` struct in a new `table` submodule:
```rust
use memory::paging::entry::*;
use memory::paging::ENTRY_COUNT;
pub struct Table {
entries: [Entry; ENTRY_COUNT],
}
```
It's just an array of 512 page table entries.
To make the `Table` indexable itself, we can implement the `Index` and `IndexMut` traits:
```rust
use core::ops::{Index, IndexMut};
impl Index<usize> for Table {
type Output = Entry;
fn index(&self, index: usize) -> &Entry {
&self.entries[index]
}
}
impl IndexMut<usize> for Table {
fn index_mut(&mut self, index: usize) -> &mut Entry {
&mut self.entries[index]
}
}
```
Now it's possible to get the 42th entry through `some_table[42]`. Of course we could replace `usize` with `u32` or even `u16` here but it would cause more numerical conversions (`x as u16`).
Let's add a method that sets all entries to unused. We will need it when we create new page tables in the future. The method looks like this:
```rust
pub fn zero(&mut self) {
for entry in self.entries.iter_mut() {
entry.set_unused();
}
}
```
Now we can read page tables and retrieve the mapping information. We can also update them through the `IndexMut` trait and the `Entry::set` method. But how do we get references to the various page tables?
We could read the `CR3` register to get the physical address of the P4 table and read its entries to get the P3 addresses. The P3 entries then point to the P2 tables and so on. But this method only works for identity-mapped pages. In the future we will create new page tables, which aren't in the identity-mapped area anymore. Since we can't access them through their physical address, we need a way to map them to virtual addresses.
## Mapping Page Tables
So how do we map the page tables itself? We don't have that problem for the current P4, P3, and P2 table since they are part of the identity-mapped area, but we need a way to access future tables, too.
One solution is to identity map all page tables. That way we would not need to differentiate virtual and physical addresses and could easily access the tables. But it clutters the virtual address space and increases fragmentation. And it makes creating page tables much more complicated since we need a physical frame whose corresponding page isn't already used for something else.
An alternative solution is to map the page tables only temporary. To read/write a page table, we would map it to some free virtual address until we're done. We could use a small pool of such virtual addresses and reuse them for various tables. This method occupies only few virtual addresses and thus is a good solution for 32-bit systems, which have small address spaces. But it makes things much more complicated since we need to temporary map up to 4 tables to access a single page. And the temporary mapping requires modification of other page tables, which need to be mapped, too.
We will solve the problem in another way using a trick called _recursive mapping_.
### Recursive Mapping
The trick is to map the P4 table recursively: The last entry doesn't point to a P3 table, but to the P4 table itself. We can use this entry to remove a translation level so that we land on a page table instead. For example, we can “loop” once to access a P1 table:
![access P1 table through recursive paging](recursive_mapping_access_p1.svg)
By selecting the 511th P4 entry, which points points to the P4 table itself, the P4 table is used as the P3 table. Similarly, the P3 table is used as a P2 table and the P2 table is treated like a P1 table. Thus the P1 table becomes the target page and can be accessed through the offset.
It's also possible to access P2 tables by looping twice. And if we select the 511th entry three times, we can access and modify P3 tables:
![access P3 table through recursive paging](recursive_mapping_access_p3.svg)
So we just need to specify the desired P3 table in the address through the P1 index. By choosing the 511th entry multiple times, we stay on the P4 table until the address's P1 index becomes the actual P4 index.
To access the P4 table itself, we loop once more and thus never leave the frame:
![access P4 table through recursive paging](recursive_mapping_access_p4.svg)
So we can access and modify page tables of all levels by just setting one P4 entry once. Most work is done by the CPU, we just the recursive entry to remove one or more translation levels. It may seem a bit strange at first, but it's a clean and simple solution once you wrapped your head around it.
By using recursive mapping, each page table is accessible through an unique virtual address. The math checks out, too: If all page tables are used, there is 1 P4 table, 511 P3 tables (the last entry is used for the recursive mapping), `511*512` P2 tables, and `511*512*512` P1 tables. So there are `134217728` page tables altogether. Each page table occupies 4KiB, so we need `134217728 * 4KiB = 512GiB` to store them. That's exactly the amount of memory that can be accessed through one P4 entry since `4KiB per page * 512 P1 entries * 512 P2 entries * 512 P3 entries = 512GiB`.
Of course recursive mapping has some disadvantages, too. It occupies a P4 entry and thus 512GiB of the virtual address space. But since we're in long mode and have a 48-bit address space, there are still 225.5TiB left. The bigger problem is that only the active table can be modified by default. To access another table, the recursive entry needs to be replaced temporary. We will tackle this problem in the next post when we switch to a new page table.
### Implementation
To map the P4 table recursively, we just need to point the 511th entry to the table itself. Of course we could do it in Rust, but it would require some fiddling with unsafe pointers. It's easier to just add some lines to our boot assembly:
```nasm
mov eax, p4_table
or eax, 0b11 ; present + writable
mov [p4_table + 511 * 8], eax
```
I put it right after the `set_up_page_tables` label, but you can add it wherever you like.
Now we can use special virtual addresses to access the page tables. The P4 table is available at `0xfffffffffffff000`. Let's add a P4 constant to the `table` submodule:
```rust
pub const P4: *mut Table = 0xffffffff_fffff000 as *mut _;
```
Let's switch to the octal system, since it makes more sense for the other special addresses. The P4 address from above is equivalent to `0o177777_777_777_777_777_0000` in octal. You can see that is has index `777` in all tables and offset `0000`. The `177777` bits on the left are the sign extension bits, which are copies of the 47th bit. They are required because x86 only uses 48bit virtual addresses.
The other tables can be accessed through the following addresses:
Table | Address | Indexes
----- | ------------------------------- | ----------------------------------
P4 | `0o177777_777_777_777_777_0000` |
P3 | `0o177777_777_777_777_XXX_0000` | `XXX` is the P4 index
P2 | `0o177777_777_777_XXX_YYY_0000` | like above, and `YYY` is the P3 index
P1 | `0o177777_777_XXX_YYY_ZZZ_0000` | like above, and `ZZZ` is the P2 index
If we look closely, we can see that the P3 address is equal to `(P4 << 9) | XXX_0000`. And the P2 address is calculated through `(P3 << 9) | YYY_0000`. So to get the next address, we need to shift it 9 bits to the left and add the table index. As a formula:
```
next_table_address = (table_address << 9) | (index << 12)
```
### The `next_table` Methods
Let's add the above formula as a `Table` method:
```rust
fn next_table_address(&self, index: usize) -> Option<usize> {
let entry_flags = self[index].flags();
if entry_flags.contains(PRESENT) && !entry_flags.contains(HUGE_PAGE) {
let table_address = self as *const _ as usize;
Some((table_address << 9) | (index << 12))
} else {
None
}
}
```
The next table address is only valid if the corresponding entry is present and does not create a huge page. Then we can do some pointer casting to get the table address and use the formula to calculate the next address.
If the index is out of bounds, the function will panic since Rust checks array bounds. The panic is desired here since a wrong index should not be possible and indicates a bug.
To convert the address into references, we add two functions:
```rust
pub fn next_table(&self, index: usize) -> Option<&Table> {
self.next_table_address(index)
.map(|address| unsafe { &*(address as *const _) })
}
pub fn next_table_mut(&mut self, index: usize) -> Option<&mut Table> {
self.next_table_address(index)
.map(|address| unsafe { &mut *(address as *mut _) })
}
```
We convert the address into raw pointers through `as` casts and then convert them into Rust references through `&mut *`. The latter is an `unsafe` operation since Rust can't guarantee that the raw pointer is valid.
Note that `self` stays borrowed as long as the returned reference is valid. This is because of Rust's [lifetime elision] rules. Basically, these rules say that the lifetime of an output reference is the same as the lifetime of the input reference by default. So the above function signatures are expanded to:
[lifetime elision]: https://doc.rust-lang.org/1.30.0/book/first-edition/lifetimes.html#lifetime-elision
```rust
pub fn next_table<'a>(&'a self, index: usize) -> Option<&'a Table> {...}
pub fn next_table_mut<'a>(&'a mut self, index: usize)
-> Option<&'a mut Table>
{...}
```
Note the additional lifetime parameters, which are identical for input and output references. That's exactly what we want. It ensures that we can't modify tables as long as we have references to lower tables. For example, it would be very bad if we could unmap a P3 table if we still write to one of its P2 tables.
#### Safety
Now we can start at the `P4` constant and use the `next_table` functions to access the lower tables. And we don't even need `unsafe` blocks to do it! Right now, your alarm bells should be ringing. Thanks to Rust, everything we've done before in this post was completely safe. But we just introduced two unsafe blocks to convince Rust that there are valid tables at the specified addresses. Can we really be sure?
First, these addresses are only valid if the P4 table is mapped recursively. Since the paging module will be the only module that modifies page tables, we can introduce an invariant for the module:
> _The 511th entry of the active P4 table must always be mapped to the active P4 table itself._
So if we switch to another P4 table at some time, it needs to be identity mapped _before_ it becomes active. As long as we obey this invariant, we can safely use the special addresses. But even with this invariant, there is a big problem with the two methods:
_What happens if we call them on a P1 table?_
Well, they would calculate the address of the next table (which does not exist) and treat it as a page table. Either they construct an invalid address (if `XXX < 400`)[^fn-invalid-address] or access the mapped page itself. That way, we could easily corrupt memory or cause CPU exceptions by accident. So these two functions are not _safe_ in Rust terms. Thus we need to make them `unsafe` functions unless we find some clever solution.
## Some Clever Solution
We can use Rust's type system to statically guarantee that the `next_table` methods can only be called on P4, P3, and P2 tables, but not on a P1 table. The idea is to add a `Level` parameter to the `Table` type and implement the `next_table` methods only for level 4, 3, and 2.
To model the levels we use a trait and empty enums:
```rust
pub trait TableLevel {}
pub enum Level4 {}
pub enum Level3 {}
pub enum Level2 {}
pub enum Level1 {}
impl TableLevel for Level4 {}
impl TableLevel for Level3 {}
impl TableLevel for Level2 {}
impl TableLevel for Level1 {}
```
An empty enum has size zero and disappears completely after compiling. Unlike an empty struct, it's not possible to instantiate an empty enum. Since we will use `TableLevel` and the table levels in exported types, they need to be public.
To differentiate the P1 table from the other tables, we introduce a `HierarchicalLevel` trait, which is a subtrait of `TableLevel`. But we implement it only for the levels 4, 3, and 2:
```rust
pub trait HierarchicalLevel: TableLevel {}
impl HierarchicalLevel for Level4 {}
impl HierarchicalLevel for Level3 {}
impl HierarchicalLevel for Level2 {}
```
Now we add the level parameter to the `Table` type:
```rust
use core::marker::PhantomData;
pub struct Table<L: TableLevel> {
entries: [Entry; ENTRY_COUNT],
level: PhantomData<L>,
}
```
We need to add a [PhantomData] field because unused type parameters are not allowed in Rust.
[PhantomData]: https://doc.rust-lang.org/core/marker/struct.PhantomData.html#unused-type-parameters
Since we changed the `Table` type, we need to update every use of it:
```rust
pub const P4: *mut Table<Level4> = 0xffffffff_fffff000 as *mut _;
...
impl<L> Table<L> where L: TableLevel
{
pub fn zero(&mut self) {...}
}
impl<L> Table<L> where L: HierarchicalLevel
{
pub fn next_table(&self, index: usize) -> Option<&Table<???>> {...}
pub fn next_table_mut(&mut self, index: usize) -> Option<&mut Table<???>>
{...}
fn next_table_address(&self, index: usize) -> Option<usize> {...}
}
impl<L> Index<usize> for Table<L> where L: TableLevel {...}
impl<L> IndexMut<usize> for Table<L> where L: TableLevel {...}
```
Now the `next_table` methods are only available for P4, P3, and P2 tables. But they have the incomplete return type `Table<???>` now. What should we fill in for the `???`?
For a P4 table we would like to return a `Table<Level3>`, for a P3 table a `Table<Level2>`, and for a P2 table a `Table<Level1>`. So we want to return a table of the _next level_.
We can define the next level by adding an associated type to the `HierarchicalLevel` trait:
```rust
trait HierarchicalLevel: TableLevel {
type NextLevel: TableLevel;
}
impl HierarchicalLevel for Level4 {
type NextLevel = Level3;
}
impl HierarchicalLevel for Level3 {
type NextLevel = Level2;
}
impl HierarchicalLevel for Level2 {
type NextLevel = Level1;
}
```
Now we can replace the `Table<???>` types with `Table<L::NextLevel>` types and our code works as intended. You can try it with a simple test function:
```rust
fn test() {
let p4 = unsafe { &*P4 };
p4.next_table(42)
.and_then(|p3| p3.next_table(1337))
.and_then(|p2| p2.next_table(0xdeadbeaf))
.and_then(|p1| p1.next_table(0xcafebabe))
}
```
Most of the indexes are completely out of bounds, so it would panic if it's called. But we don't need to call it since it already fails at compile time:
```
error: no method named `next_table` found for type
`&memory::paging::table::Table<memory::paging::table::Level1>`
in the current scope
```
Remember that this is bare metal kernel code. We just used type system magic to make low-level page table manipulations safer. Rust is just awesome!
## Translating Addresses
Now let's do something useful with our new module. We will create a function that translates a virtual address to the corresponding physical address. We add it to the `paging/mod.rs` module:
```rust
pub fn translate(virtual_address: VirtualAddress)
-> Option<PhysicalAddress>
{
let offset = virtual_address % PAGE_SIZE;
translate_page(Page::containing_address(virtual_address))
.map(|frame| frame.number * PAGE_SIZE + offset)
}
```
It uses two functions we haven't defined yet: `translate_page` and `Page::containing_address`. Let's start with the latter:
```rust
pub fn containing_address(address: VirtualAddress) -> Page {
assert!(address < 0x0000_8000_0000_0000 ||
address >= 0xffff_8000_0000_0000,
"invalid address: 0x{:x}", address);
Page { number: address / PAGE_SIZE }
}
```
The assertion is needed because there can be invalid addresses. Addresses on x86 are just 48-bit long and the other bits are just _sign extension_, i.e. a copy of the most significant bit. For example:
```
invalid address: 0x0000_8000_0000_0000
valid address: 0xffff_8000_0000_0000
└── bit 47
```
So the address space is split into two halves: the _higher half_ containing addresses with sign extension and the _lower half_ containing addresses without. Everything in between is invalid.
Since we added `containing_address`, we add the inverse method as well (maybe we need it later):
```rust
fn start_address(&self) -> usize {
self.number * PAGE_SIZE
}
```
The other missing function, `translate_page`, looks like this:
```rust
use memory::Frame;
fn translate_page(page: Page) -> Option<Frame> {
use self::entry::HUGE_PAGE;
let p3 = unsafe { &*table::P4 }.next_table(page.p4_index());
let huge_page = || {
// TODO
};
p3.and_then(|p3| p3.next_table(page.p3_index()))
.and_then(|p2| p2.next_table(page.p2_index()))
.and_then(|p1| p1[page.p1_index()].pointed_frame())
.or_else(huge_page)
}
```
We use an unsafe block to convert the raw `P4` pointer to a reference. Then we use the [Option::and_then] function to go through the four table levels. If some entry along the way is `None`, we check if the page is a huge page through the (unimplemented) `huge_page` closure.
The `Page::p*_index` functions return the different table indexes. They look like this:
[Option::and_then]: https://doc.rust-lang.org/nightly/core/option/enum.Option.html#method.and_then
```rust
fn p4_index(&self) -> usize {
(self.number >> 27) & 0o777
}
fn p3_index(&self) -> usize {
(self.number >> 18) & 0o777
}
fn p2_index(&self) -> usize {
(self.number >> 9) & 0o777
}
fn p1_index(&self) -> usize {
(self.number >> 0) & 0o777
}
```
### Safety
We use an `unsafe` block to convert the raw `P4` pointer into a shared reference. It's safe because we don't create any `&mut` references to the table right now and don't switch the P4 table either. But as soon as we do something like that, we have to revisit this method.
### Huge Pages
The `huge_page` closure calculates the corresponding frame if huge pages are used. Its content looks like this:
```rust
p3.and_then(|p3| {
let p3_entry = &p3[page.p3_index()];
// 1GiB page?
if let Some(start_frame) = p3_entry.pointed_frame() {
if p3_entry.flags().contains(HUGE_PAGE) {
// address must be 1GiB aligned
assert!(start_frame.number % (ENTRY_COUNT * ENTRY_COUNT) == 0);
return Some(Frame {
number: start_frame.number + page.p2_index() *
ENTRY_COUNT + page.p1_index(),
});
}
}
if let Some(p2) = p3.next_table(page.p3_index()) {
let p2_entry = &p2[page.p2_index()];
// 2MiB page?
if let Some(start_frame) = p2_entry.pointed_frame() {
if p2_entry.flags().contains(HUGE_PAGE) {
// address must be 2MiB aligned
assert!(start_frame.number % ENTRY_COUNT == 0);
return Some(Frame {
number: start_frame.number + page.p1_index()
});
}
}
}
None
})
```
This function is much longer and more complex than the `translate_page` function itself. To avoid this complexity in the future, we will only work with standard 4KiB pages from now on.
## Mapping Pages
Let's add a function that modifies the page tables to map a `Page` to a `Frame`:
```rust
pub use self::entry::*;
use memory::FrameAllocator;
pub fn map_to<A>(page: Page, frame: Frame, flags: EntryFlags,
allocator: &mut A)
where A: FrameAllocator
{
let p4 = unsafe { &mut *P4 };
let mut p3 = p4.next_table_create(page.p4_index(), allocator);
let mut p2 = p3.next_table_create(page.p3_index(), allocator);
let mut p1 = p2.next_table_create(page.p2_index(), allocator);
assert!(p1[page.p1_index()].is_unused());
p1[page.p1_index()].set(frame, flags | PRESENT);
}
```
We add an re-export for all `entry` types since they are required to call the function. We assert that the page is unmapped and always set the present flag (since it wouldn't make sense to map a page without setting it).
The `Table::next_table_create` method doesn't exist yet. It should return the next table if it exists, or create a new one. For the implementation we need the `FrameAllocator` from the [previous post] and the `Table::zero` method:
```rust
use memory::FrameAllocator;
pub fn next_table_create<A>(&mut self,
index: usize,
allocator: &mut A)
-> &mut Table<L::NextLevel>
where A: FrameAllocator
{
if self.next_table(index).is_none() {
assert!(!self.entries[index].flags().contains(HUGE_PAGE),
"mapping code does not support huge pages");
let frame = allocator.allocate_frame().expect("no frames available");
self.entries[index].set(frame, PRESENT | WRITABLE);
self.next_table_mut(index).unwrap().zero();
}
self.next_table_mut(index).unwrap()
}
```
We can use `unwrap()` here since the next table definitely exists.
### Safety
We used an `unsafe` block in `map_to` to convert the raw `P4` pointer to a `&mut` reference. That's bad. It's now possible that the `&mut` reference is not exclusive, which breaks Rust's guarantees. It's only a matter time before we run into a data race. For example, imagine that one thread maps an entry to `frame_A` and another thread (on the same core) tries to map the same entry to `frame_B`.
The problem is that there's no clear _owner_ for the page tables. So let's define page table ownership!
### Page Table Ownership
We define the following:
> A page table owns all of its subtables.
We already obey this rule: To get a reference to a table, we need to borrow it from its parent table through the `next_table` method. But who owns the P4 table?
> The recursively mapped P4 table is owned by a `ActivePageTable` struct.
We just defined some random owner for the P4 table. But it will solve our problems. And it will also provide the interface to other modules.
So let's create the struct:
```rust
use self::table::{Table, Level4};
use core::ptr::Unique;
pub struct ActivePageTable {
p4: Unique<Table<Level4>>,
}
```
We can't store the `Table<Level4>` directly because it needs to be at a special memory location (like the [VGA text buffer]). We could use a raw pointer or `&mut` instead of [Unique], but Unique indicates ownership better.
[VGA text buffer]: @/edition-1/posts/04-printing-to-screen/index.md#the-text-buffer
[Unique]: https://doc.rust-lang.org/1.10.0/core/ptr/struct.Unique.html
Because the `ActivePageTable` owns the unique recursive mapped P4 table, there must be only one `ActivePageTable` instance. Thus we make the constructor function unsafe:
```rust
impl ActivePageTable {
pub unsafe fn new() -> ActivePageTable {
ActivePageTable {
p4: Unique::new_unchecked(table::P4),
}
}
}
```
We add some methods to get P4 references:
```rust
fn p4(&self) -> &Table<Level4> {
unsafe { self.p4.as_ref() }
}
fn p4_mut(&mut self) -> &mut Table<Level4> {
unsafe { self.p4.as_mut() }
}
```
Since we will only create valid P4 pointers, the `unsafe` blocks are safe. However, we don't make these functions public since they can be used to make page tables invalid. Only the higher level functions (such as `translate` or `map_to`) should be usable from other modules.
Now we can make the `map_to` and `translate` functions safe by making them methods of `ActivePageTable`:
```rust
impl ActivePageTable {
pub unsafe fn new() -> ActivePageTable {...}
fn p4(&self) -> &Table<Level4> {...}
fn p4_mut(&mut self) -> &mut Table<Level4> {...}
pub fn translate(&self, virtual_address: VirtualAddress)
-> Option<PhysicalAddress>
{
...
self.translate_page(...).map(...)
}
fn translate_page(&self, page: Page) -> Option<Frame> {
let p3 = self.p4().next_table(...);
...
}
pub fn map_to<A>(&mut self,
page: Page,
frame: Frame,
flags: EntryFlags,
allocator: &mut A)
where A: FrameAllocator
{
let mut p3 = self.p4_mut().next_table_create(...);
...
}
}
```
Now the `p4()` and `p4_mut()` methods should be the only methods containing an `unsafe` block in the `paging/mod.rs` file.
### More Mapping Functions
For convenience, we add a `map` method that just picks a free frame for us:
```rust
pub fn map<A>(&mut self, page: Page, flags: EntryFlags, allocator: &mut A)
where A: FrameAllocator
{
let frame = allocator.allocate_frame().expect("out of memory");
self.map_to(page, frame, flags, allocator)
}
```
We also add a `identity_map` function to make it easier to remap the kernel in the next post:
```rust
pub fn identity_map<A>(&mut self,
frame: Frame,
flags: EntryFlags,
allocator: &mut A)
where A: FrameAllocator
{
let page = Page::containing_address(frame.start_address());
self.map_to(page, frame, flags, allocator)
}
```
### Unmapping Pages
To unmap a page, we set the corresponding P1 entry to unused:
```rust
fn unmap<A>(&mut self, page: Page, allocator: &mut A)
where A: FrameAllocator
{
assert!(self.translate(page.start_address()).is_some());
let p1 = self.p4_mut()
.next_table_mut(page.p4_index())
.and_then(|p3| p3.next_table_mut(page.p3_index()))
.and_then(|p2| p2.next_table_mut(page.p2_index()))
.expect("mapping code does not support huge pages");
let frame = p1[page.p1_index()].pointed_frame().unwrap();
p1[page.p1_index()].set_unused();
// TODO free p(1,2,3) table if empty
allocator.deallocate_frame(frame);
}
```
The assertion ensures that the page is mapped. Thus the corresponding P1 table and frame must exist for a standard 4KiB page. We set the entry to unused and free the associated frame in the supplied frame allocator.
We can also free the P1, P2, or even P3 table when the last entry is freed. But checking the whole table on every `unmap` would be very expensive. So we leave the `TODO` in place until we find a good solution. I'm open for suggestions :).
_Spoiler_: There is an ugly bug in this function, which we will find in the next section.
## Testing and Bugfixing
To test it, we add a `test_paging` function in `memory/paging/mod.rs`:
```rust
pub fn test_paging<A>(allocator: &mut A)
where A: FrameAllocator
{
let mut page_table = unsafe { ActivePageTable::new() };
// test it
}
```
We borrow the frame allocator since we will need it for the mapping functions. To be able to call that function from main, we need to re-export it in `memory/mod.rs`:
```rust
// in memory/mod.rs
pub use self::paging::test_paging;
// lib.rs
let mut frame_allocator = ...;
memory::test_paging(&mut frame_allocator);
```
### map_to
Let's test the `map_to` function:
```rust
let addr = 42 * 512 * 512 * 4096; // 42th P3 entry
let page = Page::containing_address(addr);
let frame = allocator.allocate_frame().expect("no more frames");
println!("None = {:?}, map to {:?}",
page_table.translate(addr),
frame);
page_table.map_to(page, frame, EntryFlags::empty(), allocator);
println!("Some = {:?}", page_table.translate(addr));
println!("next free frame: {:?}", allocator.allocate_frame());
```
We just map some random page to a free frame. To be able to borrow the page table as `&mut`, we need to make it mutable.
You should see output similar to this:
```
None = None, map to Frame { number: 0 }
Some = Some(0)
next free frame: Some(Frame { number: 3 })
```
It's frame 0 because it's the first frame returned by the frame allocator. Since we map the 42th P3 entry, the mapping code needs to create a P2 and a P1 table. So the next free frame returned by the allocator is frame 3.
### unmap
To test the `unmap` function, we unmap the test page so that it translates to `None` again:
```rust
page_table.unmap(Page::containing_address(addr), allocator);
println!("None = {:?}", page_table.translate(addr));
```
It causes a panic since we call the unimplemented `deallocate_frame` method in `unmap`. If we comment this call out, it works without problems. But there is some bug in this function nevertheless.
Let's read something from the mapped page (of course before we unmap it again):
```rust
println!("{:#x}", unsafe {
*(Page::containing_address(addr).start_address() as *const u64)
});
```
Since we don't zero the mapped pages, the output is random. For me, it's `0xf000ff53f000ff53`.
If `unmap` worked correctly, reading it again after unmapping should cause a page fault. But it doesn't. Instead, it just prints the same number again. When we remove the first read, we get the desired page fault (i.e. QEMU reboots again and again). So this seems to be some cache issue.
An x86 processor has many different caches because always accessing the main memory would be very slow. Most of these caches are completely _transparent_. That means everything works exactly the same as without them, it's just much faster. But there is one cache, that needs to be updated manually: the _translation lookaside buffer_.
The translation lookaside buffer, or TLB, caches the translation of virtual to physical addresses. It's filled automatically when a page is accessed. But it's not updated transparently when the mapping of a page changes. This is the reason that we still can access the page even through we unmapped it in the page table.
So to fix our `unmap` function, we need to remove the cached translation from the TLB. We can use the [x86_64][x86_64 crate] crate to do this easily. To add it, we append the following to our `Cargo.toml`:
[x86_64 crate]: https://docs.rs/x86_64
```toml
[dependencies]
...
x86_64 = "0.1.2"
```
Now we can use it to fix `unmap`:
```rust
...
p1[page.p1_index()].set_unused();
use x86_64::instructions::tlb;
use x86_64::VirtualAddress;
tlb::flush(VirtualAddress(page.start_address()));
// TODO free p(1,2,3) table if empty
//allocator.deallocate_frame(frame);
}
```
Now the desired page fault occurs even when we access the page before.
## Conclusion
This post has become pretty long. So let's summarize what we've done:
- we created a paging module and modeled page tables plus entries
- we mapped the P4 page recursively and created `next_table` methods
- we used empty enums and associated types to make the `next_table` functions safe
- we wrote a function to translate virtual to physical addresses
- we created safe functions to map and unmap pages
- and we fixed stack overflow and TLB related bugs
## What's next?
In the [next post] we will extend this module and add a function to modify inactive page tables. Through that function, we will create a new page table hierarchy that maps the kernel correctly using 4KiB pages. Then we will switch to the new table to get a safer kernel environment.
[next post]: @/edition-1/posts/07-remap-the-kernel/index.md
Afterwards, we will use this paging module to build a heap allocator. This will allow us to use allocation and collection types such as `Box` and `Vec`.
<small>Image sources: [^virtual_physical_translation_source]</small>
## Footnotes
[^fn-invalid-address]: If the `XXX` part of the address is smaller than `0o400`, it's binary representation doesn't start with `1`. But the sign extension bits, which should be a copy of that bit, are `1` instead of `0`. Thus the address is not valid.
[^virtual_physical_translation_source]: Image sources: Modified versions of an image from [Wikipedia](https://commons.wikimedia.org/wiki/File:X86_Paging_64bit.svg). The modified files are licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -1,819 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="900.00409"
height="530"
viewBox="-2141 2141 12027.89 7032.1014"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="recursive_mapping_access_p3.svg">
<metadata
id="metadata370">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs368" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="998"
id="namedview366"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="2.7755576e-17"
fit-margin-bottom="0"
inkscape:zoom="1"
inkscape:cx="236.56561"
inkscape:cy="267.91265"
inkscape:window-x="1080"
inkscape:window-y="568"
inkscape:window-maximized="1"
inkscape:current-layer="g4"
units="in" />
<g
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="g4"
transform="translate(-1087.0637,717.51407)">
<polyline
points="3365,2598 3365,3070"
id="polyline10"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3189,2598 3189,3070"
id="polyline12"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3012,2598 3012,3070"
id="polyline14"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2834,2598 2834,3070"
id="polyline16"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2657,2598 2657,3070"
id="polyline18"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2480,2598 2480,3070"
id="polyline20"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2303,2598 2303,3070"
id="polyline22"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2125,2456 2125,3070"
id="polyline24"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1948,2598 1948,3070"
id="polyline26"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1772,2598 1772,3070"
id="polyline28"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1594,2598 1594,3070"
id="polyline30"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1417,2598 1417,3070"
id="polyline32"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1239,2598 1239,3070"
id="polyline34"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1063,2598 1063,3070"
id="polyline36"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="886,2598 886,3070"
id="polyline38"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="708,2456 708,3070"
id="polyline40"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3543,2456 3543,3070"
id="polyline74"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="-2125,2598 3543,2598"
id="polyline76"
transform="matrix(0.70622791,0,0,1.3955346,455.54989,-1956.6141)" />
<polyline
points="3543,3070 -1948,3070 -2125,3070"
id="polyline78"
transform="matrix(0.70351457,0,0,1.3955346,465.16321,-1956.6141)" />
<polyline
points="9035,2598 9035,3070"
id="polyline80"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8858,2598 8858,3070"
id="polyline82"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8681,2598 8681,3070"
id="polyline84"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8503,2598 8503,3070"
id="polyline86"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8326,2598 8326,3070"
id="polyline88"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8150,2598 8150,3070"
id="polyline90"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7972,2598 7972,3070"
id="polyline92"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7795,2456 7795,3070"
id="polyline94"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7617,2598 7617,3070"
id="polyline96"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7441,2598 7441,3070"
id="polyline98"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7264,2598 7264,3070"
id="polyline100"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7086,2598 7086,3070"
id="polyline102"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6909,2598 6909,3070"
id="polyline104"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6732,2598 6732,3070"
id="polyline106"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6555,2598 6555,3070"
id="polyline108"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6377,2456 6377,3070"
id="polyline110"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6200,2598 6200,3070"
id="polyline112"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6024,2598 6024,3070"
id="polyline114"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5846,2598 5846,3070"
id="polyline116"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5669,2598 5669,3070"
id="polyline118"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5491,2598 5491,3070"
id="polyline120"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5315,2598 5315,3070"
id="polyline122"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5138,2598 5138,3070"
id="polyline124"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4960,2409 4960,3070"
id="polyline126"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4783,2598 4783,3070"
id="polyline128"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4606,2598 4606,3070"
id="polyline130"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4429,2598 4429,3070"
id="polyline132"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4251,2598 4251,3070"
id="polyline134"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4074,2598 4074,3070"
id="polyline136"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3898,2598 3898,3070"
id="polyline138"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3720,2598 3720,3070"
id="polyline140"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="9212,2456 9212,3070"
id="polyline142"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3543,2598 9212,2598"
id="polyline144"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="9212,3070 3720,3070 0,3070"
id="polyline146"
transform="matrix(1.3047439,0,0,1.3955346,-1051.0007,-1956.6141)" />
<rect
x="4021.6787"
y="4311.5088"
width="1668.8131"
height="3296.2527"
rx="0"
style="fill:#dfdfdf;stroke-width:11.23412323"
id="rect150" />
<polyline
points="3118,3590 2834,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline178"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1322,3590 1039,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline180"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4913,3590 4629,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline184"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6519,3590 6236,3779"
style="stroke:#0000ff"
id="polyline186"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8314,3590 8031,3779"
style="stroke:#0000ff"
id="polyline188"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
style="stroke:#0000ff"
id="polyline204"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline206"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
style="stroke:#0000ff"
id="polyline208"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline210"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline212"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<g
style="fill:#000000;stroke-width:0"
id="g242"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
<text
xml:space="preserve"
x="5546.7881"
y="6876.4409"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-5">0</tspan></text>
<text
xml:space="preserve"
x="3507"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text246"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
<text
xml:space="preserve"
x="2161"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text248"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
<text
xml:space="preserve"
x="2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text250"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
<text
xml:space="preserve"
x="744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text252"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
<text
xml:space="preserve"
x="672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text254"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text256"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="-744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text258"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text260"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="9176"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text262"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
<text
xml:space="preserve"
x="7759"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text264"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
<text
xml:space="preserve"
x="6342"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text266"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
<text
xml:space="preserve"
x="4924"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text268"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
<text
xml:space="preserve"
x="3579"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text270"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
<text
xml:space="preserve"
x="6413"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text272"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
<text
xml:space="preserve"
x="7830"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text274"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
<text
xml:space="preserve"
x="4996"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text276"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
<g
transform="matrix(0,-1,1,0,8976,6614)"
id="g282" />
<text
xml:space="preserve"
x="-2125"
y="2314"
font-style="normal"
font-weight="normal"
font-size="152"
id="text290"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<g
transform="matrix(0,-1,1,0,4916.9545,5254.403)"
id="g306">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="3874"
y="3968"
font-style="normal"
font-weight="normal"
font-size="152"
id="text318"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
xml:space="preserve"
x="4862.9551"
y="4397.5566"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46391296px"
id="tspan3503">P4 table</tspan></text>
<text
x="2031"
y="5622"
font-style="normal"
font-weight="normal"
font-size="152"
id="text346"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
x="3165"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text350"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
<text
x="1370"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text352"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
<text
x="1370"
y="6708"
font-style="normal"
font-weight="normal"
font-size="152"
id="text354"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
<text
x="4960"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text356"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
<text
x="6566"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text358"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
<text
x="8362"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text360"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
<text
x="1181"
y="7532.2715"
font-style="normal"
font-weight="normal"
font-size="152"
id="text364"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<text
x="1946.0945"
y="6998.52"
font-style="normal"
font-weight="normal"
font-size="152"
id="text344-8-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%">
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3493-3-2" />
</text>
<rect
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
x="4294.9473"
y="4493.6152"
width="1181"
height="471.99994"
rx="0"
id="rect162-5" />
<circle
cx="5335.9546"
cy="4720.2134"
style="fill:#008200;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
id="circle172-4"
r="46.999996" />
<text
xml:space="preserve"
x="4815.9546"
y="4773.6704"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46391296px;stroke-width:0"
id="tspan3495-6">P4 entry</tspan></text>
<text
xml:space="preserve"
x="4925.6357"
y="7173.5298"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-0"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46391296px;stroke-width:0"
id="tspan3495-6-4">Recursive</tspan></text>
<text
xml:space="preserve"
x="4925.6357"
y="7440.8149"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-0-9"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46391296px;stroke-width:0"
id="tspan3495-6-4-4">Mapping</tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:6.53430033;stroke-linecap:butt;stroke-linejoin:miter"
x="4296.5396"
y="5560.2471"
width="1182.4656"
height="314.50067"
rx="0"
id="rect162-5-1" />
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-0.99999994,0.99999994,0,4915.3734,6277.1653)"
id="g306-2">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308-3"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="3097.1609"
y="2954.5652"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-4"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
inkscape:transform-center-x="-789.14735"
inkscape:transform-center-y="1300.106"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3503-4">111111111111111111111111111</tspan></text>
<rect
x="8208.377"
y="4073.4419"
width="691.0863"
height="3268.5144"
rx="0"
style="fill:#dfdfdf;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
id="rect8" />
<rect
x="8208.377"
y="5474.9399"
width="691.0863"
height="233.25354"
rx="0"
style="fill:#dfdfdf;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
id="rect158" />
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-0.98836234,0.97611052,0,9106.1205,5822.055)"
id="g286">
<text
xml:space="preserve"
x="96.892052"
y="38.756821"
font-style="normal"
font-weight="normal"
font-size="152"
id="text288"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46391296px"
id="tspan3509">P3 table </tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.53380773;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 2308.9448,-2797.8507 -483.0169,0 0,-2544.6425 -1879.786585,0 0,339.1222"
id="path8874"
inkscape:connector-curvature="0" />
</g>
<text
xml:space="preserve"
x="4809.9756"
y="5774.4951"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-5"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46392822px;stroke-width:0"
id="tspan3495-6-7">P4 entry</tspan></text>
<ellipse
ry="46.999996"
rx="46.999992"
cx="5336.855"
cy="5714.1816"
style="fill:#0000ff;stroke:#0000ff;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter"
id="circle172-4-4" />
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:9.55735683px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 5392.0276,5712.0004 1512.0496,0 0,1631.4957 1257.8109,0"
id="path9464"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 8173.2452,3542.5202 0,364.8071 -584.0141,0 0,1802.0952 528.0052,0"
id="path9466"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
x="5610.3569"
y="4990.1772"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5">511</tspan></text>
</g>
<polygon
points="3308,6614 3150,6566 3150,6661 3150,6661 "
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
id="polygon232-2"
transform="matrix(1.4130509,0,0,1.3955346,-646.4618,-1622.6914)" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.76697159px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 5574.5569,4637.287 719.9709,0 0,3526.7653 -2765.702,0 0,-560.545 294.8212,0"
id="path5354"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.52740288px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3843.9848,5007.8363 -4223.58417,0 0,-2024.2908"
id="path5356"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.65159035px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2156.7076,2986.8221 0,1988.2634 1691.863,0"
id="path5358"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.78581619px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 4696.351,2980.052 0,497.5653 -1975.7645,0 0,1464.6642 1128.3337,0"
id="path5360"
inkscape:connector-curvature="0" />
<polygon
points="3150,6661 3308,6614 3150,6566 3150,6661 "
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="polygon232-2-7"
transform="matrix(1.4130509,0,0,1.3955346,-625.83944,-2988.2046)" />
<polygon
points="3150,6661 3150,6661 3308,6614 3150,6566 "
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="polygon232-2-7-3"
transform="matrix(1.4130509,0,0,1.3955346,4891.7749,-3219.444)" />
<polygon
points="3150,6661 3150,6661 3308,6614 3150,6566 "
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="polygon232-2-7-3-5"
transform="matrix(1.4130509,0,0,1.3955346,4896.989,-940.27671)" />
<polygon
points="3150,6566 3150,6661 3150,6661 3308,6614 "
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
id="polygon232-2-7-7"
transform="matrix(1.4130509,0,0,1.3955346,-628.09888,-4256.4264)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,765 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="900.00409"
height="530"
viewBox="-2141 2141 12027.89 7032.1014"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="recursive_mapping_access_p4.svg">
<metadata
id="metadata370">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs368" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="998"
id="namedview366"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="2.7755576e-17"
fit-margin-bottom="0"
inkscape:zoom="0.5"
inkscape:cx="150.51041"
inkscape:cy="507.56343"
inkscape:window-x="1080"
inkscape:window-y="568"
inkscape:window-maximized="1"
inkscape:current-layer="g4"
units="in" />
<g
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="g4"
transform="translate(-1087.0637,717.51407)">
<polyline
points="3365,2598 3365,3070"
id="polyline10"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3189,2598 3189,3070"
id="polyline12"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3012,2598 3012,3070"
id="polyline14"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2834,2598 2834,3070"
id="polyline16"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2657,2598 2657,3070"
id="polyline18"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2480,2598 2480,3070"
id="polyline20"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2303,2598 2303,3070"
id="polyline22"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2125,2456 2125,3070"
id="polyline24"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1948,2598 1948,3070"
id="polyline26"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1772,2598 1772,3070"
id="polyline28"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1594,2598 1594,3070"
id="polyline30"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1417,2598 1417,3070"
id="polyline32"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1239,2598 1239,3070"
id="polyline34"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1063,2598 1063,3070"
id="polyline36"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="886,2598 886,3070"
id="polyline38"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="708,2456 708,3070"
id="polyline40"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3543,2456 3543,3070"
id="polyline74"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="-2125,2598 3543,2598"
id="polyline76"
transform="matrix(0.70622791,0,0,1.3955346,455.54989,-1956.6141)" />
<polyline
points="3543,3070 -1948,3070 -2125,3070"
id="polyline78"
transform="matrix(0.70351457,0,0,1.3955346,465.16321,-1956.6141)" />
<polyline
points="9035,2598 9035,3070"
id="polyline80"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8858,2598 8858,3070"
id="polyline82"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8681,2598 8681,3070"
id="polyline84"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8503,2598 8503,3070"
id="polyline86"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8326,2598 8326,3070"
id="polyline88"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8150,2598 8150,3070"
id="polyline90"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7972,2598 7972,3070"
id="polyline92"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7795,2456 7795,3070"
id="polyline94"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7617,2598 7617,3070"
id="polyline96"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7441,2598 7441,3070"
id="polyline98"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7264,2598 7264,3070"
id="polyline100"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7086,2598 7086,3070"
id="polyline102"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6909,2598 6909,3070"
id="polyline104"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6732,2598 6732,3070"
id="polyline106"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6555,2598 6555,3070"
id="polyline108"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6377,2456 6377,3070"
id="polyline110"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6200,2598 6200,3070"
id="polyline112"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6024,2598 6024,3070"
id="polyline114"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5846,2598 5846,3070"
id="polyline116"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5669,2598 5669,3070"
id="polyline118"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5491,2598 5491,3070"
id="polyline120"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5315,2598 5315,3070"
id="polyline122"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5138,2598 5138,3070"
id="polyline124"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4960,2409 4960,3070"
id="polyline126"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4783,2598 4783,3070"
id="polyline128"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4606,2598 4606,3070"
id="polyline130"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4429,2598 4429,3070"
id="polyline132"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4251,2598 4251,3070"
id="polyline134"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4074,2598 4074,3070"
id="polyline136"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3898,2598 3898,3070"
id="polyline138"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3720,2598 3720,3070"
id="polyline140"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="9212,2456 9212,3070"
id="polyline142"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3543,2598 9212,2598"
id="polyline144"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="9212,3070 3720,3070 0,3070"
id="polyline146"
transform="matrix(1.3047439,0,0,1.3955346,-1051.0007,-1956.6141)" />
<rect
x="4021.6787"
y="4311.5088"
width="1668.8131"
height="3296.2527"
rx="0"
style="fill:#dfdfdf;stroke-width:11.23412323"
id="rect150" />
<polyline
points="3118,3590 2834,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline178"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1322,3590 1039,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline180"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4913,3590 4629,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline184"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6519,3590 6236,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline186"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8314,3590 8031,3779"
style="stroke:#0000ff"
id="polyline188"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
style="stroke:#0000ff"
id="polyline204"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline206"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline208"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline210"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline212"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<g
style="fill:#000000;stroke-width:0"
id="g242"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
<text
xml:space="preserve"
x="3507"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text246"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
<text
xml:space="preserve"
x="2161"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text248"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
<text
xml:space="preserve"
x="2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text250"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
<text
xml:space="preserve"
x="744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text252"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
<text
xml:space="preserve"
x="672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text254"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text256"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="-744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text258"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text260"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="9176"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text262"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
<text
xml:space="preserve"
x="7759"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text264"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
<text
xml:space="preserve"
x="6342"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text266"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
<text
xml:space="preserve"
x="4924"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text268"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
<text
xml:space="preserve"
x="3579"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text270"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
<text
xml:space="preserve"
x="6413"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text272"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
<text
xml:space="preserve"
x="7830"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text274"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
<text
xml:space="preserve"
x="4996"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text276"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
<g
transform="matrix(0,-1,1,0,8976,6614)"
id="g282" />
<text
xml:space="preserve"
x="-2125"
y="2314"
font-style="normal"
font-weight="normal"
font-size="152"
id="text290"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<g
transform="matrix(0,-1,1,0,4916.9545,5254.403)"
id="g306">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="3874"
y="3968"
font-style="normal"
font-weight="normal"
font-size="152"
id="text318"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
xml:space="preserve"
x="4862.9551"
y="4397.5566"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46391296px"
id="tspan3503">P4 table</tspan></text>
<text
x="2031"
y="5622"
font-style="normal"
font-weight="normal"
font-size="152"
id="text346"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
x="3165"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text350"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
<text
x="1370"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text352"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
<text
x="1370"
y="6708"
font-style="normal"
font-weight="normal"
font-size="152"
id="text354"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
<text
x="4960"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text356"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
<text
x="6566"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text358"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
<text
x="8362"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text360"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
<text
x="1181"
y="7532.2715"
font-style="normal"
font-weight="normal"
font-size="152"
id="text364"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<text
x="1946.0945"
y="6998.52"
font-style="normal"
font-weight="normal"
font-size="152"
id="text344-8-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%">
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3493-3-2" />
</text>
<rect
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
x="4294.9473"
y="4493.6152"
width="1181"
height="471.99994"
rx="0"
id="rect162-5" />
<circle
cx="5335.9546"
cy="4720.2134"
style="fill:#008200;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
id="circle172-4"
r="46.999996" />
<text
xml:space="preserve"
x="4815.9546"
y="4773.6704"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46391296px;stroke-width:0"
id="tspan3495-6">P4 entry</tspan></text>
<text
xml:space="preserve"
x="4925.6357"
y="7173.5298"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-0"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46391296px;stroke-width:0"
id="tspan3495-6-4">Recursive</tspan></text>
<text
xml:space="preserve"
x="4925.6357"
y="7440.8149"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-0-9"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46391296px;stroke-width:0"
id="tspan3495-6-4-4">Mapping</tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:6.53430033;stroke-linecap:butt;stroke-linejoin:miter"
x="4296.5396"
y="5560.2471"
width="1182.4656"
height="314.50067"
rx="0"
id="rect162-5-1" />
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-0.99999994,0.99999994,0,4915.3734,6277.1653)"
id="g306-2">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308-3"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="3891.613"
y="2954.5652"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-4"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
inkscape:transform-center-x="-789.14735"
inkscape:transform-center-y="1300.106"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3503-4">111111111111111111111111111111111111</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:13.36426067px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 5603.8041,5874.9118 2568.0264,0 0,-2337.6838"
id="path5364"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
x="7481.9214"
y="6083.3872"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-0-6"
style="font-style:normal;font-weight:normal;font-size:151.99998474px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46389771px;stroke-width:0"
id="tspan3495-6-4-9">Offset in bytes</tspan></text>
<text
xml:space="preserve"
x="5622.4961"
y="5000.6953"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5">511</tspan></text>
<text
xml:space="preserve"
x="5548.4839"
y="6877.3652"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-1">0</tspan></text>
</g>
<polygon
transform="matrix(-1.4130475,-0.00314156,0.00318097,-1.395531,10325.825,15480.806)"
points="3150,6661 3308,6614 3150,6566 3150,6661 "
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="polygon232-0-6" />
<polygon
points="3308,6614 3150,6566 3150,6661 3150,6661 "
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
id="polygon232-2"
transform="matrix(1.4130509,0,0,1.3955346,-646.4618,-1622.6914)" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.76697159px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 5574.5569,4637.287 719.9709,0 0,3526.7653 -2765.702,0 0,-560.545 294.8212,0"
id="path5354"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.54883385px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3842.7968,5013.0895 -4222.39215,0 0,-2029.5495"
id="path5356"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.67419624px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2156.6937,2986.8005 0,2001.4832 1684.7642,0"
id="path5358"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.88186646px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 4696.3171,2980.021 0,502.6836 -1975.6967,0 0,1479.7303 1128.295,0"
id="path5360"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:19.05912971px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 6962.3822,2986.9474 0,745.9077 -3590.1899,0 0,1203.077 448.1838,0"
id="path5362"
inkscape:connector-curvature="0" />
<polygon
points="3150,6661 3308,6614 3150,6566 3150,6661 "
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
id="polygon232-2-3"
transform="matrix(1.4130509,0,0,1.3955346,-639.67391,-4258.2588)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -1,917 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="900.0036"
height="145.45076"
viewBox="-2141 2141 12027.883 1929.8575"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="x86_address_structure.svg">
<metadata
id="metadata370">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs368" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="716"
id="namedview366"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="2.8284271"
inkscape:cx="84.319072"
inkscape:cy="113.89333"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g242"
units="px" />
<g
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="g4"
transform="translate(331.35503,-225.83102)">
<polyline
points="3365,2598 3365,3070"
id="polyline10"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="3189,2598 3189,3070"
id="polyline12"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="3012,2598 3012,3070"
id="polyline14"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="2834,2598 2834,3070"
id="polyline16"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="2657,2598 2657,3070"
id="polyline18"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="2480,2598 2480,3070"
id="polyline20"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="2303,2598 2303,3070"
id="polyline22"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="2125,2456 2125,3070"
id="polyline24"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:25.36408559;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="1948,2598 1948,3070"
id="polyline26"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="1772,2598 1772,3070"
id="polyline28"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="1594,2598 1594,3070"
id="polyline30"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="1417,2598 1417,3070"
id="polyline32"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="1239,2598 1239,3070"
id="polyline34"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="1063,2598 1063,3070"
id="polyline36"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="886,2598 886,3070"
id="polyline38"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="708,2456 708,3070"
id="polyline40"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:25.36408559;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="3543,2456 3543,3070"
id="polyline74"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:25.36408559;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="-2125,2598 3543,2598"
id="polyline76"
transform="matrix(0.52969427,0,0,1.0477876,1661.7387,-151.61082)"
style="stroke-width:17.93889427;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="3543,3070 -1948,3070 -2125,3070"
id="polyline78"
transform="matrix(0.52765919,0,0,1.0477876,1673.588,-157.78641)"
style="stroke-width:17.97345503;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="9035,2598 9035,3070"
id="polyline80"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="8858,2598 8858,3070"
id="polyline82"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="8681,2598 8681,3070"
id="polyline84"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="8503,2598 8503,3070"
id="polyline86"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="8326,2598 8326,3070"
id="polyline88"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="8150,2598 8150,3070"
id="polyline90"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="7972,2598 7972,3070"
id="polyline92"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="7795,2456 7795,3070"
id="polyline94"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:25.36408559;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="7617,2598 7617,3070"
id="polyline96"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="7441,2598 7441,3070"
id="polyline98"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="7264,2598 7264,3070"
id="polyline100"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="7086,2598 7086,3070"
id="polyline102"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="6909,2598 6909,3070"
id="polyline104"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="6732,2598 6732,3070"
id="polyline106"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="6555,2598 6555,3070"
id="polyline108"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="6377,2456 6377,3070"
id="polyline110"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:25.36408559;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="6200,2598 6200,3070"
id="polyline112"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="6024,2598 6024,3070"
id="polyline114"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="5846,2598 5846,3070"
id="polyline116"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="5669,2598 5669,3070"
id="polyline118"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="5491,2598 5491,3070"
id="polyline120"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="5315,2598 5315,3070"
id="polyline122"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="5138,2598 5138,3070"
id="polyline124"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="4960,2409 4960,3070"
id="polyline126"
transform="matrix(1.0598349,0,0,0.97582891,-211.91044,63.126762)"
style="stroke-width:25.36408615;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="4783,2598 4783,3070"
id="polyline128"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="4606,2598 4606,3070"
id="polyline130"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="4429,2598 4429,3070"
id="polyline132"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="4251,2598 4251,3070"
id="polyline134"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="4074,2598 4074,3070"
id="polyline136"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="3898,2598 3898,3070"
id="polyline138"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="3720,2598 3720,3070"
id="polyline140"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="9212,2456 9212,3070"
id="polyline142"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
style="stroke-width:25.36408559;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="3543,2598 9212,2598"
id="polyline144"
transform="matrix(1.0598349,0,0,1.0477876,-207.27136,-151.61082)"
style="stroke-width:17.75485992;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="9212,3070 3720,3070 0,3070"
id="polyline146"
transform="matrix(0.65314228,0,0,1.0477876,3541.5006,-157.82019)"
style="stroke-width:18.04502869;stroke-miterlimit:4;stroke-dasharray:none" />
<polyline
points="3118,3590 2834,3779"
style="stroke:#0000ff"
id="polyline178"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
<polyline
points="1322,3590 1039,3779"
style="stroke:#0000ff"
id="polyline180"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
<polyline
points="4913,3590 4629,3779"
style="stroke:#0000ff"
id="polyline184"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
<polyline
points="6519,3590 6236,3779"
style="stroke:#0000ff"
id="polyline186"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
<polyline
points="8314,3590 8031,3779"
style="stroke:#0000ff"
id="polyline188"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
<polyline
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
style="stroke:#0000ff"
id="polyline204"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
<polyline
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
style="stroke:#0000ff"
id="polyline206"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
<polyline
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
style="stroke:#0000ff"
id="polyline208"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
<polyline
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
style="stroke:#0000ff"
id="polyline210"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
<polyline
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
style="stroke:#0000ff"
id="polyline212"
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
<g
style="fill:#000000;stroke-width:0"
id="g242">
<text
xml:space="preserve"
x="3484.9529"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text246"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">32</text>
<text
xml:space="preserve"
x="2305.7319"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text248"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">39</text>
<text
xml:space="preserve"
x="1965.1508"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text250"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">40</text>
<text
xml:space="preserve"
x="480.46173"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text252"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">48</text>
<text
xml:space="preserve"
x="672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text254"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text256"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="-744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text258"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text260"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="9458.9111"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text262"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">0</text>
<text
xml:space="preserve"
x="7965.6851"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text264"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">8</text>
<text
xml:space="preserve"
x="6445.8828"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text266"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">16</text>
<text
xml:space="preserve"
x="4978.1792"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text268"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">24</text>
<text
xml:space="preserve"
x="3796.0706"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text270"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">31</text>
<text
xml:space="preserve"
x="6782.5229"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text272"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">15</text>
<text
xml:space="preserve"
x="8167.3521"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text274"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">7</text>
<text
xml:space="preserve"
x="5289.2969"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text276"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">23</text>
<text
xml:space="preserve"
x="-2125"
y="2314"
font-style="normal"
font-weight="normal"
font-size="152"
id="text290"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<text
xml:space="preserve"
x="3874"
y="3968"
font-style="normal"
font-weight="normal"
font-size="152"
id="text318"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
x="2031"
y="5622"
font-style="normal"
font-weight="normal"
font-size="152"
id="text346"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
x="1074.5779"
y="4269.3325"
font-style="normal"
font-weight="normal"
font-size="152"
id="text348"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3501">P4 index</tspan>
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan5052" />
</text>
<text
x="3124.5554"
y="3774.0684"
font-style="normal"
font-weight="normal"
font-size="152"
id="text350"
style="font-style:normal;font-weight:normal;font-size:160.17668152px;font-family:Helvetica;text-anchor:start;fill:#0000ff;stroke-width:0"
transform="scale(1.0057325,0.99430016)">9</text>
<text
x="1232.9951"
y="3774.0684"
font-style="normal"
font-weight="normal"
font-size="152"
id="text352"
style="font-style:normal;font-weight:normal;font-size:160.17668152px;font-family:Helvetica;text-anchor:start;fill:#0000ff;stroke-width:0"
transform="scale(1.0057325,0.99430016)">9</text>
<text
x="1370"
y="6708"
font-style="normal"
font-weight="normal"
font-size="152"
id="text354"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
<text
x="5016.1157"
y="3774.0684"
font-style="normal"
font-weight="normal"
font-size="152"
id="text356"
style="font-style:normal;font-weight:normal;font-size:160.17668152px;font-family:Helvetica;text-anchor:start;fill:#0000ff;stroke-width:0"
transform="scale(1.0057325,0.99430016)">9</text>
<text
x="6708.5088"
y="3774.0684"
font-style="normal"
font-weight="normal"
font-size="152"
id="text358"
style="font-style:normal;font-weight:normal;font-size:160.17668152px;font-family:Helvetica;text-anchor:start;fill:#0000ff;stroke-width:0"
transform="scale(1.0057325,0.99430016)">9</text>
<text
x="8601.1221"
y="3774.0684"
font-style="normal"
font-weight="normal"
font-size="152"
id="text360"
style="font-style:normal;font-weight:normal;font-size:160.17668152px;font-family:Helvetica;text-anchor:start;fill:#0000ff;stroke-width:0"
transform="scale(1.0057325,0.99430016)">12</text>
<text
x="1181"
y="7532.2715"
font-style="normal"
font-weight="normal"
font-size="152"
id="text364"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<text
xml:space="preserve"
x="809.5191"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text252-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">47</text>
<text
xml:space="preserve"
x="-684.95245"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text252-4"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">55</text>
<text
xml:space="preserve"
x="-1034.7118"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text252-4-7"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">56</text>
<text
xml:space="preserve"
x="-2201.2915"
y="2529.5376"
font-style="normal"
font-weight="normal"
font-size="152"
id="text252-4-7-3"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">63</text>
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-2192.9027,-160.09714)"
points="1322,3590 1039,3779"
style="fill:none;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="polyline180-0" />
<text
x="-736.70618"
y="3771.7437"
font-style="normal"
font-weight="normal"
font-size="152"
id="text352-2"
style="font-style:normal;font-weight:normal;font-size:160.17668152px;font-family:Helvetica;text-anchor:start;fill:#0000ff;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="scale(1.0057325,0.99430016)">16</text>
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.08317852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -929.72072,3549.5659 0,431.3931"
id="path4175"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.08317852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 1039.4204,3550.0043 0,431.3933"
id="path4175-3"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.08317852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2942.7798,3549.8477 0,431.3932"
id="path4175-3-8"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.08317852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 4844.585,3551.0064 0,431.3933"
id="path4175-3-8-6"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.08317852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 6546.975,3551.1192 0,431.3932"
id="path4175-3-8-6-3"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.08317852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 8449.877,3550.1734 0,431.3931"
id="path4175-3-8-6-3-5"
inkscape:connector-curvature="0" />
<text
x="-980.58942"
y="4269.3325"
font-style="normal"
font-weight="normal"
font-size="152"
id="text348-3"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3501-0">Sign extension</tspan>
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan5052-5" />
</text>
<text
x="4862.6738"
y="4269.3325"
font-style="normal"
font-weight="normal"
font-size="152"
id="text348-1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3501-5">P2 index</tspan>
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan5052-8" />
</text>
<text
x="2919.9609"
y="4269.3325"
font-style="normal"
font-weight="normal"
font-size="152"
id="text348-1-3"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3501-5-8">P3 index</tspan>
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan5052-8-0" />
</text>
<text
x="6532.3008"
y="4269.3325"
font-style="normal"
font-weight="normal"
font-size="152"
id="text348-1-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3501-5-9">P1 index</tspan>
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan5052-8-7" />
</text>
<text
x="8392.0518"
y="4269.3325"
font-style="normal"
font-weight="normal"
font-size="152"
id="text348-1-5-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="scale(1.0057325,0.99430017)"
sodipodi:linespacing="125%">
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3501-5-9-5">Offset</tspan>
</text>
</g>
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="3365,2598 3365,3070"
id="polyline10-3" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="3189,2598 3189,3070"
id="polyline12-5" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="3012,2598 3012,3070"
id="polyline14-6" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="2834,2598 2834,3070"
id="polyline16-9" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="2657,2598 2657,3070"
id="polyline18-9" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="2480,2598 2480,3070"
id="polyline20-8" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="2303,2598 2303,3070"
id="polyline22-2" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:25.36408559;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="2125,2456 2125,3070"
id="polyline24-5" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="1948,2598 1948,3070"
id="polyline26-2" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="1772,2598 1772,3070"
id="polyline28-1" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="1594,2598 1594,3070"
id="polyline30-5" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="1417,2598 1417,3070"
id="polyline32-9" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="1239,2598 1239,3070"
id="polyline34-3" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="1063,2598 1063,3070"
id="polyline36-9" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="886,2598 886,3070"
id="polyline38-2" />
<polyline
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
style="fill:none;stroke:#000000;stroke-width:25.36408559;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="708,2456 708,3070"
id="polyline40-6" />
<polyline
style="fill:none;stroke:#000000;stroke-width:17.93889618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="-2125,2598 3543,2598"
id="polyline76-6"
transform="matrix(0.52969421,0,0,1.0477875,-1349.4687,-150.28545)" />
<polyline
style="fill:none;stroke:#000000;stroke-width:17.97345674;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
points="3543,3070 -1948,3070 -2125,3070"
id="polyline78-8"
transform="matrix(0.52765914,0,0,1.0477875,-1332.9803,-157.56699)" />
<polyline
transform="matrix(1.4116086,0,0,1.0450205,-12466.637,-150.55138)"
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
style="fill:none;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="polyline204-3" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -1,404 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="617.39313"
height="215.65028"
viewBox="-2141 2141 8251.0033 2861.2729"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="cyclic_mapping_inactive_table.svg">
<metadata
id="metadata370">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs368" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="998"
id="namedview366"
showgrid="false"
fit-margin-top="1"
fit-margin-left="1"
fit-margin-right="1"
fit-margin-bottom="1"
inkscape:zoom="1.4142136"
inkscape:cx="345.80952"
inkscape:cy="50.097583"
inkscape:window-x="1080"
inkscape:window-y="413"
inkscape:window-maximized="1"
inkscape:current-layer="g242"
units="in" />
<g
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="g4"
transform="translate(-1516.6308,-1533.1047)">
<g
style="fill:#000000;stroke-width:0"
id="g242"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
<text
xml:space="preserve"
x="672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text254"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text256"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="-744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text258"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text260"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<g
transform="matrix(0,-1,1,0,8976,6614)"
id="g282" />
<text
xml:space="preserve"
x="-2125"
y="2314"
font-style="normal"
font-weight="normal"
font-size="152"
id="text290"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<text
xml:space="preserve"
x="3874"
y="3968"
font-style="normal"
font-weight="normal"
font-size="152"
id="text318"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
x="2031"
y="5622"
font-style="normal"
font-weight="normal"
font-size="152"
id="text346"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
x="1370"
y="6708"
font-style="normal"
font-weight="normal"
font-size="152"
id="text354"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
<text
x="1181"
y="7532.2715"
font-style="normal"
font-weight="normal"
font-size="152"
id="text364"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<text
x="1946.0945"
y="6998.52"
font-style="normal"
font-weight="normal"
font-size="152"
id="text344-8-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%">
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3493-3-2" />
</text>
<rect
x="3090.1064"
y="4497.9272"
width="1182.895"
height="1367.9473"
rx="0"
style="fill:#dfdfdf;stroke:#000000;stroke-width:6.0930233;stroke-linecap:butt;stroke-linejoin:miter"
id="rect150-8" />
<text
xml:space="preserve"
x="4341.8867"
y="5887.8105"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-5-9"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-5-6">0</tspan></text>
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-1,1,0,3712.0549,5414.9455)"
id="g306-4">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308-1"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="3676.9697"
y="4404.876"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-8"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46392822px;stroke-width:0"
id="tspan3503-3">P4 table</tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
x="3090.0466"
y="4500.9346"
width="1181"
height="471.99997"
rx="0"
id="rect162-5-4" />
<text
xml:space="preserve"
x="3611.0537"
y="4780.9897"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-1"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46392822px;stroke-width:0"
id="tspan3495-6-41">P4 entry</tspan></text>
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-0.99999992,0.9999999,0,2296.6633,6284.4847)"
id="g306-2-3" />
<text
xml:space="preserve"
x="4405.4561"
y="4997.4966"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-8">511</tspan></text>
<ellipse
ry="46.999996"
rx="46.999992"
cx="4117.4102"
cy="4723.3125"
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="circle172-4-3" />
<text
xml:space="preserve"
x="3674.7217"
y="4178.6655"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-8-5"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46394348px;stroke-width:0"
id="tspan3503-3-0">active</tspan></text>
<rect
x="5370.3823"
y="4498.0884"
width="1182.895"
height="1367.9473"
rx="0"
style="fill:#dfdfbe;fill-opacity:1;stroke:#000000;stroke-width:6.0930233;stroke-linecap:butt;stroke-linejoin:miter"
id="rect150-8-9" />
<text
xml:space="preserve"
x="6622.1631"
y="5887.9717"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-5-9-3"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-5-6-5">0</tspan></text>
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-1,1,0,5992.3314,5415.1068)"
id="g306-4-9">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308-1-8"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="5957.2456"
y="4405.0371"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-8-0"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46394348px;stroke-width:0"
id="tspan3503-3-2">P4 table</tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
x="5370.3228"
y="4501.0957"
width="1181"
height="472"
rx="0"
id="rect162-5-4-4" />
<text
xml:space="preserve"
x="5891.3296"
y="4781.1509"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-1-6"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46394348px;stroke-width:0"
id="tspan3495-6-41-6">P4 entry</tspan></text>
<text
xml:space="preserve"
x="6685.7324"
y="4997.6577"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-6-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-8-1">511</tspan></text>
<polygon
points="3308,6614 3150,6566 3150,6661 3150,6661 "
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="polygon232-2-7-7-1-1"
transform="translate(2076.0084,-749.88898)" />
<ellipse
ry="46.999996"
rx="46.999992"
cx="6397.6865"
cy="4723.4736"
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="circle172-4-3-6" />
<text
xml:space="preserve"
x="5954.9976"
y="4178.8267"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-8-5-9"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46394348px;stroke-width:0"
id="tspan3503-3-0-2">inactive</tspan></text>
<rect
x="1019.4597"
y="4821.9668"
width="1487.6132"
height="469.84174"
rx="0"
style="fill:#dfdfdf;stroke:#000000;stroke-width:4.0044775;stroke-linecap:butt;stroke-linejoin:miter"
id="rect150-8-0" />
<text
xml:space="preserve"
x="1642.8574"
y="5136.835"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-1-4"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46394348px;stroke-width:0"
id="tspan3495-6-41-8">CR3 register</tspan></text>
<ellipse
ry="46.999996"
rx="46.999992"
cx="2349.6458"
cy="5076.04"
style="fill:#969696;fill-opacity:1;stroke:#969696;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="circle172-4-3-2" />
<path
style="fill:none;fill-rule:evenodd;stroke:#969696;stroke-width:9.42606354px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2349.6459,5073.5559 359.3939,0 0,777.3949 255.3589,0"
id="path4612"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 4118.0344,4723.1182 672.1066,0 0,1137.6237 494.8846,0"
id="path4614"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:9.51690578px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 6398.2284,4730.6471 434.6959,0 0,1347.5425 -4005.8897,0 0,-197.3547 140.4403,0"
id="path4641"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<polygon
points="3308,6614 3150,6566 3150,6661 3150,6661 "
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="polygon232-2-7-7-1-1-7"
transform="translate(-214.18472,-748.49788)" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -1,819 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="900.00409"
height="530"
viewBox="-2141 2141 12027.89 7032.1014"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="recursive_mapping_access_p3.svg">
<metadata
id="metadata370">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs368" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="998"
id="namedview366"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="2.7755576e-17"
fit-margin-bottom="0"
inkscape:zoom="1"
inkscape:cx="236.56561"
inkscape:cy="267.91265"
inkscape:window-x="1080"
inkscape:window-y="568"
inkscape:window-maximized="1"
inkscape:current-layer="g4"
units="in" />
<g
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="g4"
transform="translate(-1087.0637,717.51407)">
<polyline
points="3365,2598 3365,3070"
id="polyline10"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3189,2598 3189,3070"
id="polyline12"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3012,2598 3012,3070"
id="polyline14"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2834,2598 2834,3070"
id="polyline16"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2657,2598 2657,3070"
id="polyline18"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2480,2598 2480,3070"
id="polyline20"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2303,2598 2303,3070"
id="polyline22"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2125,2456 2125,3070"
id="polyline24"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1948,2598 1948,3070"
id="polyline26"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1772,2598 1772,3070"
id="polyline28"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1594,2598 1594,3070"
id="polyline30"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1417,2598 1417,3070"
id="polyline32"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1239,2598 1239,3070"
id="polyline34"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1063,2598 1063,3070"
id="polyline36"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="886,2598 886,3070"
id="polyline38"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="708,2456 708,3070"
id="polyline40"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3543,2456 3543,3070"
id="polyline74"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="-2125,2598 3543,2598"
id="polyline76"
transform="matrix(0.70622791,0,0,1.3955346,455.54989,-1956.6141)" />
<polyline
points="3543,3070 -1948,3070 -2125,3070"
id="polyline78"
transform="matrix(0.70351457,0,0,1.3955346,465.16321,-1956.6141)" />
<polyline
points="9035,2598 9035,3070"
id="polyline80"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8858,2598 8858,3070"
id="polyline82"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8681,2598 8681,3070"
id="polyline84"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8503,2598 8503,3070"
id="polyline86"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8326,2598 8326,3070"
id="polyline88"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8150,2598 8150,3070"
id="polyline90"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7972,2598 7972,3070"
id="polyline92"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7795,2456 7795,3070"
id="polyline94"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7617,2598 7617,3070"
id="polyline96"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7441,2598 7441,3070"
id="polyline98"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7264,2598 7264,3070"
id="polyline100"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7086,2598 7086,3070"
id="polyline102"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6909,2598 6909,3070"
id="polyline104"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6732,2598 6732,3070"
id="polyline106"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6555,2598 6555,3070"
id="polyline108"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6377,2456 6377,3070"
id="polyline110"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6200,2598 6200,3070"
id="polyline112"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6024,2598 6024,3070"
id="polyline114"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5846,2598 5846,3070"
id="polyline116"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5669,2598 5669,3070"
id="polyline118"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5491,2598 5491,3070"
id="polyline120"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5315,2598 5315,3070"
id="polyline122"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5138,2598 5138,3070"
id="polyline124"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4960,2409 4960,3070"
id="polyline126"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4783,2598 4783,3070"
id="polyline128"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4606,2598 4606,3070"
id="polyline130"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4429,2598 4429,3070"
id="polyline132"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4251,2598 4251,3070"
id="polyline134"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4074,2598 4074,3070"
id="polyline136"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3898,2598 3898,3070"
id="polyline138"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3720,2598 3720,3070"
id="polyline140"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="9212,2456 9212,3070"
id="polyline142"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3543,2598 9212,2598"
id="polyline144"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="9212,3070 3720,3070 0,3070"
id="polyline146"
transform="matrix(1.3047439,0,0,1.3955346,-1051.0007,-1956.6141)" />
<rect
x="4021.6787"
y="4311.5088"
width="1668.8131"
height="3296.2527"
rx="0"
style="fill:#dfdfdf;stroke-width:11.23412323"
id="rect150" />
<polyline
points="3118,3590 2834,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline178"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1322,3590 1039,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline180"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4913,3590 4629,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline184"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6519,3590 6236,3779"
style="stroke:#0000ff"
id="polyline186"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8314,3590 8031,3779"
style="stroke:#0000ff"
id="polyline188"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
style="stroke:#0000ff"
id="polyline204"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline206"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
style="stroke:#0000ff"
id="polyline208"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline210"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline212"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<g
style="fill:#000000;stroke-width:0"
id="g242"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
<text
xml:space="preserve"
x="5546.7881"
y="6876.4409"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-5">0</tspan></text>
<text
xml:space="preserve"
x="3507"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text246"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
<text
xml:space="preserve"
x="2161"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text248"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
<text
xml:space="preserve"
x="2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text250"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
<text
xml:space="preserve"
x="744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text252"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
<text
xml:space="preserve"
x="672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text254"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text256"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="-744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text258"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text260"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="9176"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text262"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
<text
xml:space="preserve"
x="7759"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text264"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
<text
xml:space="preserve"
x="6342"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text266"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
<text
xml:space="preserve"
x="4924"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text268"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
<text
xml:space="preserve"
x="3579"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text270"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
<text
xml:space="preserve"
x="6413"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text272"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
<text
xml:space="preserve"
x="7830"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text274"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
<text
xml:space="preserve"
x="4996"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text276"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
<g
transform="matrix(0,-1,1,0,8976,6614)"
id="g282" />
<text
xml:space="preserve"
x="-2125"
y="2314"
font-style="normal"
font-weight="normal"
font-size="152"
id="text290"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<g
transform="matrix(0,-1,1,0,4916.9545,5254.403)"
id="g306">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="3874"
y="3968"
font-style="normal"
font-weight="normal"
font-size="152"
id="text318"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
xml:space="preserve"
x="4862.9551"
y="4397.5566"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46391296px"
id="tspan3503">P4 table</tspan></text>
<text
x="2031"
y="5622"
font-style="normal"
font-weight="normal"
font-size="152"
id="text346"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
x="3165"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text350"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
<text
x="1370"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text352"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
<text
x="1370"
y="6708"
font-style="normal"
font-weight="normal"
font-size="152"
id="text354"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
<text
x="4960"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text356"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
<text
x="6566"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text358"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
<text
x="8362"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text360"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
<text
x="1181"
y="7532.2715"
font-style="normal"
font-weight="normal"
font-size="152"
id="text364"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<text
x="1946.0945"
y="6998.52"
font-style="normal"
font-weight="normal"
font-size="152"
id="text344-8-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%">
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3493-3-2" />
</text>
<rect
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
x="4294.9473"
y="4493.6152"
width="1181"
height="471.99994"
rx="0"
id="rect162-5" />
<circle
cx="5335.9546"
cy="4720.2134"
style="fill:#008200;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
id="circle172-4"
r="46.999996" />
<text
xml:space="preserve"
x="4815.9546"
y="4773.6704"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46391296px;stroke-width:0"
id="tspan3495-6">P4 entry</tspan></text>
<text
xml:space="preserve"
x="4925.6357"
y="7173.5298"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-0"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46391296px;stroke-width:0"
id="tspan3495-6-4">Recursive</tspan></text>
<text
xml:space="preserve"
x="4925.6357"
y="7440.8149"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-0-9"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46391296px;stroke-width:0"
id="tspan3495-6-4-4">Mapping</tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:6.53430033;stroke-linecap:butt;stroke-linejoin:miter"
x="4296.5396"
y="5560.2471"
width="1182.4656"
height="314.50067"
rx="0"
id="rect162-5-1" />
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-0.99999994,0.99999994,0,4915.3734,6277.1653)"
id="g306-2">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308-3"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="3097.1609"
y="2954.5652"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-4"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
inkscape:transform-center-x="-789.14735"
inkscape:transform-center-y="1300.106"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3503-4">111111111111111111111111111</tspan></text>
<rect
x="8208.377"
y="4073.4419"
width="691.0863"
height="3268.5144"
rx="0"
style="fill:#dfdfdf;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
id="rect8" />
<rect
x="8208.377"
y="5474.9399"
width="691.0863"
height="233.25354"
rx="0"
style="fill:#dfdfdf;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
id="rect158" />
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-0.98836234,0.97611052,0,9106.1205,5822.055)"
id="g286">
<text
xml:space="preserve"
x="96.892052"
y="38.756821"
font-style="normal"
font-weight="normal"
font-size="152"
id="text288"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46391296px"
id="tspan3509">P3 table </tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.53380773;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 2308.9448,-2797.8507 -483.0169,0 0,-2544.6425 -1879.786585,0 0,339.1222"
id="path8874"
inkscape:connector-curvature="0" />
</g>
<text
xml:space="preserve"
x="4809.9756"
y="5774.4951"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-5"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46392822px;stroke-width:0"
id="tspan3495-6-7">P4 entry</tspan></text>
<ellipse
ry="46.999996"
rx="46.999992"
cx="5336.855"
cy="5714.1816"
style="fill:#0000ff;stroke:#0000ff;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter"
id="circle172-4-4" />
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:9.55735683px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 5392.0276,5712.0004 1512.0496,0 0,1631.4957 1257.8109,0"
id="path9464"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 8173.2452,3542.5202 0,364.8071 -584.0141,0 0,1802.0952 528.0052,0"
id="path9466"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
x="5610.3569"
y="4990.1772"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5">511</tspan></text>
</g>
<polygon
points="3308,6614 3150,6566 3150,6661 3150,6661 "
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
id="polygon232-2"
transform="matrix(1.4130509,0,0,1.3955346,-646.4618,-1622.6914)" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.76697159px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 5574.5569,4637.287 719.9709,0 0,3526.7653 -2765.702,0 0,-560.545 294.8212,0"
id="path5354"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.52740288px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3843.9848,5007.8363 -4223.58417,0 0,-2024.2908"
id="path5356"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.65159035px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2156.7076,2986.8221 0,1988.2634 1691.863,0"
id="path5358"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.78581619px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 4696.351,2980.052 0,497.5653 -1975.7645,0 0,1464.6642 1128.3337,0"
id="path5360"
inkscape:connector-curvature="0" />
<polygon
points="3150,6661 3308,6614 3150,6566 3150,6661 "
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="polygon232-2-7"
transform="matrix(1.4130509,0,0,1.3955346,-625.83944,-2988.2046)" />
<polygon
points="3150,6661 3150,6661 3308,6614 3150,6566 "
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="polygon232-2-7-3"
transform="matrix(1.4130509,0,0,1.3955346,4891.7749,-3219.444)" />
<polygon
points="3150,6661 3150,6661 3308,6614 3150,6566 "
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="polygon232-2-7-3-5"
transform="matrix(1.4130509,0,0,1.3955346,4896.989,-940.27671)" />
<polygon
points="3150,6566 3150,6661 3150,6661 3308,6614 "
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
id="polygon232-2-7-7"
transform="matrix(1.4130509,0,0,1.3955346,-628.09888,-4256.4264)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,936 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="900.00409"
height="530.5"
viewBox="-2141 2141 12027.89 7038.7354"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="recursive_mapping_access_p3_inactive_table.svg">
<metadata
id="metadata370">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs368" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="998"
id="namedview366"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="1"
inkscape:zoom="1"
inkscape:cx="466.92372"
inkscape:cy="267.78109"
inkscape:window-x="1080"
inkscape:window-y="413"
inkscape:window-maximized="1"
inkscape:current-layer="g242"
units="in" />
<g
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="g4"
transform="translate(-1087.0637,710.26871)">
<polyline
points="3365,2598 3365,3070"
id="polyline10"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3189,2598 3189,3070"
id="polyline12"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3012,2598 3012,3070"
id="polyline14"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2834,2598 2834,3070"
id="polyline16"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2657,2598 2657,3070"
id="polyline18"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2480,2598 2480,3070"
id="polyline20"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2303,2598 2303,3070"
id="polyline22"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2125,2456 2125,3070"
id="polyline24"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1948,2598 1948,3070"
id="polyline26"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1772,2598 1772,3070"
id="polyline28"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1594,2598 1594,3070"
id="polyline30"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1417,2598 1417,3070"
id="polyline32"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1239,2598 1239,3070"
id="polyline34"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1063,2598 1063,3070"
id="polyline36"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="886,2598 886,3070"
id="polyline38"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="708,2456 708,3070"
id="polyline40"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3543,2456 3543,3070"
id="polyline74"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="-2125,2598 3543,2598"
id="polyline76"
transform="matrix(0.70622791,0,0,1.3955346,455.54989,-1956.6141)" />
<polyline
points="3543,3070 -1948,3070 -2125,3070"
id="polyline78"
transform="matrix(0.70351457,0,0,1.3955346,465.16321,-1956.6141)" />
<polyline
points="9035,2598 9035,3070"
id="polyline80"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8858,2598 8858,3070"
id="polyline82"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8681,2598 8681,3070"
id="polyline84"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8503,2598 8503,3070"
id="polyline86"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8326,2598 8326,3070"
id="polyline88"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8150,2598 8150,3070"
id="polyline90"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7972,2598 7972,3070"
id="polyline92"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7795,2456 7795,3070"
id="polyline94"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7617,2598 7617,3070"
id="polyline96"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7441,2598 7441,3070"
id="polyline98"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7264,2598 7264,3070"
id="polyline100"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="7086,2598 7086,3070"
id="polyline102"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6909,2598 6909,3070"
id="polyline104"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6732,2598 6732,3070"
id="polyline106"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6555,2598 6555,3070"
id="polyline108"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6377,2456 6377,3070"
id="polyline110"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6200,2598 6200,3070"
id="polyline112"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6024,2598 6024,3070"
id="polyline114"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5846,2598 5846,3070"
id="polyline116"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5669,2598 5669,3070"
id="polyline118"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5491,2598 5491,3070"
id="polyline120"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5315,2598 5315,3070"
id="polyline122"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5138,2598 5138,3070"
id="polyline124"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4960,2409 4960,3070"
id="polyline126"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4783,2598 4783,3070"
id="polyline128"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4606,2598 4606,3070"
id="polyline130"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4429,2598 4429,3070"
id="polyline132"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4251,2598 4251,3070"
id="polyline134"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4074,2598 4074,3070"
id="polyline136"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3898,2598 3898,3070"
id="polyline138"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3720,2598 3720,3070"
id="polyline140"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="9212,2456 9212,3070"
id="polyline142"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3543,2598 9212,2598"
id="polyline144"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="9212,3070 3720,3070 0,3070"
id="polyline146"
transform="matrix(1.3047439,0,0,1.3955346,-1051.0007,-1956.6141)" />
<rect
x="5478.3828"
y="4304.8267"
width="1668.8131"
height="3296.2527"
rx="0"
style="fill:#dfdfc7;fill-opacity:1;stroke-width:11.23412323"
id="rect150" />
<polyline
points="3118,3590 2834,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline178"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="1322,3590 1039,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline180"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="4913,3590 4629,3779"
style="stroke:#008200;stroke-opacity:1"
id="polyline184"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="6519,3590 6236,3779"
style="stroke:#0000ff"
id="polyline186"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="8314,3590 8031,3779"
style="stroke:#0000ff"
id="polyline188"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
style="stroke:#0000ff"
id="polyline204"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline206"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
style="stroke:#0000ff"
id="polyline208"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline210"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<polyline
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
style="stroke:#008200;stroke-opacity:1"
id="polyline212"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
<g
style="fill:#000000;stroke-width:0"
id="g242"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
<text
xml:space="preserve"
x="6577.6826"
y="6866.8647"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-5">0</tspan></text>
<text
xml:space="preserve"
x="3507"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text246"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
<text
xml:space="preserve"
x="2161"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text248"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
<text
xml:space="preserve"
x="2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text250"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
<text
xml:space="preserve"
x="744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text252"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
<text
xml:space="preserve"
x="672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text254"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text256"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="-744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text258"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text260"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="9176"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text262"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
<text
xml:space="preserve"
x="7759"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text264"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
<text
xml:space="preserve"
x="6342"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text266"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
<text
xml:space="preserve"
x="4924"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text268"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
<text
xml:space="preserve"
x="3579"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text270"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
<text
xml:space="preserve"
x="6413"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text272"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
<text
xml:space="preserve"
x="7830"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text274"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
<text
xml:space="preserve"
x="4996"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text276"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
<g
transform="matrix(0,-1,1,0,8976,6614)"
id="g282" />
<text
xml:space="preserve"
x="-2125"
y="2314"
font-style="normal"
font-weight="normal"
font-size="152"
id="text290"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<g
transform="matrix(0,-1,1,0,5947.8484,5249.6147)"
id="g306">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="3874"
y="3968"
font-style="normal"
font-weight="normal"
font-size="152"
id="text318"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
xml:space="preserve"
x="5912.7651"
y="4431.0742"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
style="font-size:200.46391296px"
id="tspan3503">P4 table</tspan></text>
<text
x="2031"
y="5622"
font-style="normal"
font-weight="normal"
font-size="152"
id="text346"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
x="3165"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text350"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1">9</text>
<text
x="1370"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text352"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1">9</text>
<text
x="1370"
y="6708"
font-style="normal"
font-weight="normal"
font-size="152"
id="text354"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
<text
x="4960"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text356"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1">9</text>
<text
x="6566"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text358"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
<text
x="8362"
y="3732"
font-style="normal"
font-weight="normal"
font-size="152"
id="text360"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
<text
x="1181"
y="7532.2715"
font-style="normal"
font-weight="normal"
font-size="152"
id="text364"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<text
x="1946.0945"
y="6998.52"
font-style="normal"
font-weight="normal"
font-size="152"
id="text344-8-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%">
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3493-3-2" />
</text>
<rect
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
x="5325.8418"
y="4484.0391"
width="1181"
height="471.99994"
rx="0"
id="rect162-5" />
<circle
cx="6366.8491"
cy="4715.4253"
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="circle172-4"
r="46.999996" />
<text
xml:space="preserve"
x="5846.8491"
y="4768.8823"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46391296px;stroke-width:0"
id="tspan3495-6">P4 entry</tspan></text>
<text
xml:space="preserve"
x="5956.5303"
y="7168.7417"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-0"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46391296px;stroke-width:0"
id="tspan3495-6-4">Recursive</tspan></text>
<text
xml:space="preserve"
x="5956.5303"
y="7436.0269"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-0-9"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46391296px;stroke-width:0"
id="tspan3495-6-4-4">Mapping</tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:6.53430033;stroke-linecap:butt;stroke-linejoin:miter"
x="5327.4341"
y="5550.6709"
width="1182.4656"
height="314.50067"
rx="0"
id="rect162-5-1" />
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-0.99999994,0.99999994,0,5946.2673,6272.377)"
id="g306-2">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308-3"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="3097.1609"
y="2954.5652"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-4"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
inkscape:transform-center-x="-789.14735"
inkscape:transform-center-y="1300.106"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3503-4">111111111111111111111111111</tspan></text>
<rect
x="8208.377"
y="4073.4419"
width="691.0863"
height="3268.5144"
rx="0"
style="fill:#dfdfc7;fill-opacity:1;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
id="rect8" />
<rect
x="8208.377"
y="5474.9399"
width="691.0863"
height="233.25354"
rx="0"
style="fill:#dfdfc7;fill-opacity:1;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
id="rect158" />
<text
xml:space="preserve"
x="5840.8701"
y="5769.707"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-5"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46392822px;stroke-width:0"
id="tspan3495-6-7">P4 entry</tspan></text>
<ellipse
ry="46.999996"
rx="46.999992"
cx="6367.7495"
cy="5709.3936"
style="fill:#0000ff;stroke:#0000ff;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter"
id="circle172-4-4" />
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 8173.2452,3542.5202 0,364.8071 -584.0141,0 0,1802.0952 528.0052,0"
id="path9466"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
x="6641.2515"
y="4980.6011"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5">511</tspan></text>
<rect
x="1677.2448"
y="4498.876"
width="1181"
height="2361.9998"
rx="0"
style="fill:#dfdfdf;stroke:#000000;stroke-width:7.99999905;stroke-linecap:butt;stroke-linejoin:miter"
id="rect150-8" />
<text
xml:space="preserve"
x="2928.0776"
y="6883.7607"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-5-9"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-5-6">0</tspan></text>
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-1,1,0,2298.2444,5414.9455)"
id="g306-4">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308-1"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="2263.1606"
y="4404.876"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-8"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46392822px;stroke-width:0"
id="tspan3503-3">P4 table</tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
x="1676.2374"
y="4500.9346"
width="1181"
height="471.99997"
rx="0"
id="rect162-5-4" />
<text
xml:space="preserve"
x="2197.2446"
y="4780.9897"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-1"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46392822px;stroke-width:0"
id="tspan3495-6-41">P4 entry</tspan></text>
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-0.99999992,0.9999999,0,2296.6633,6284.4847)"
id="g306-2-3" />
<text
xml:space="preserve"
x="2991.647"
y="4997.4966"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-8">511</tspan></text>
<polygon
points="3150,6661 3308,6614 3150,6566 3150,6661 "
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="polygon232-2-7-7-1"
transform="translate(-1613.3498,-1640.66)" />
<text
transform="matrix(0,-1.0062563,0.9937826,0,0,0)"
xml:space="preserve"
x="-5614.5532"
y="9191.6416"
font-style="normal"
font-weight="normal"
font-size="152"
id="text288"
style="font-style:normal;font-weight:normal;font-size:149.29702759px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:196.89912415px;stroke-width:0"
id="tspan3509">inactive P3 table </tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:11.42028618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1180.4935,3543.3667 0,1428.1431 381.7959,0"
id="path6515"
inkscape:connector-curvature="0" />
<ellipse
ry="46.999996"
rx="46.999992"
cx="2703.6011"
cy="4723.3125"
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="circle172-4-3" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:14.74861526;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2716.9762,4721.7506 729.3182,0 0,2110.8251 1739.3012,0"
id="path6534"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:11.42028618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 4771.7501,3542.097 0,1413.9884 417.9767,0"
id="path6542"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:11.42028618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2975.9128,3541.8854 0,541.514 1015.0566,0 0,897.233 1213.8046,0"
id="path6544"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:11.42028618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 6376.72,3540.8786 0,343.9797 -1325.7614,2.3941 0,1978.7627 172.2064,0"
id="path6546"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:11.42028618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 6378.641,5717.2755 889.0271,0 0,1623.2076 851.1962,0"
id="path6548"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
x="2260.9126"
y="4178.6655"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-8-5"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46394348px;stroke-width:0"
id="tspan3503-3-0">active</tspan></text>
<text
xml:space="preserve"
x="5915.9365"
y="4225.4873"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-0"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46392822px;stroke-width:0"
id="tspan3503-9">inactive</tspan></text>
</g>
<polygon
points="3150,6661 3150,6661 3308,6614 3150,6566 "
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="polygon232-2"
transform="matrix(1.4130509,0,0,1.3955346,810.24264,-1636.0556)" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.76342201;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 7031.2608,4630.6049 719.9709,0 0,3526.7653 -2765.702,0 2.3625,-539.2826 285.3712,0"
id="path5354"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<polygon
points="3150,6661 3308,6614 3150,6566 3150,6661 "
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="polygon232-2-7"
transform="matrix(1.4130509,0,0,1.3955346,830.86496,-2994.8867)" />
<polygon
points="3150,6661 3150,6661 3308,6614 3150,6566 "
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="polygon232-2-7-3"
transform="matrix(1.4130509,0,0,1.3955346,4891.7749,-3219.444)" />
<polygon
points="3150,6661 3150,6661 3308,6614 3150,6566 "
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="polygon232-2-7-3-5"
transform="matrix(1.4130509,0,0,1.3955346,4896.989,-940.27671)" />
<polygon
points="3150,6566 3150,6661 3150,6661 3308,6614 "
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="polygon232-2-7-7"
transform="matrix(1.4130509,0,0,1.3955346,828.60556,-4263.1085)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -1,403 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="617.77704"
height="216.27831"
viewBox="-2141 2141 8256.134 2869.6056"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="recursive_mapping_inactive_table_scheme.svg">
<metadata
id="metadata370">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs368" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="998"
id="namedview366"
showgrid="false"
fit-margin-top="1"
fit-margin-left="1"
fit-margin-right="1"
fit-margin-bottom="1"
inkscape:zoom="1.4142136"
inkscape:cx="523.64687"
inkscape:cy="50.722008"
inkscape:window-x="1080"
inkscape:window-y="413"
inkscape:window-maximized="1"
inkscape:current-layer="g242"
units="in" />
<g
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
id="g4"
transform="translate(-1516.6308,-1533.0567)">
<g
style="fill:#000000;stroke-width:0"
id="g242"
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
<text
xml:space="preserve"
x="672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text254"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-672"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text256"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<text
xml:space="preserve"
x="-744"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text258"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
<text
xml:space="preserve"
x="-2090"
y="2551"
font-style="normal"
font-weight="normal"
font-size="152"
id="text260"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
<g
transform="matrix(0,-1,1,0,8976,6614)"
id="g282" />
<text
xml:space="preserve"
x="-2125"
y="2314"
font-style="normal"
font-weight="normal"
font-size="152"
id="text290"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<text
xml:space="preserve"
x="3874"
y="3968"
font-style="normal"
font-weight="normal"
font-size="152"
id="text318"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
x="2031"
y="5622"
font-style="normal"
font-weight="normal"
font-size="152"
id="text346"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
<text
x="1370"
y="6708"
font-style="normal"
font-weight="normal"
font-size="152"
id="text354"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
<text
x="1181"
y="7532.2715"
font-style="normal"
font-weight="normal"
font-size="152"
id="text364"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
<text
x="1946.0945"
y="6998.52"
font-style="normal"
font-weight="normal"
font-size="152"
id="text344-8-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%">
<tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
id="tspan3493-3-2" />
</text>
<rect
x="3090.1064"
y="4497.9272"
width="1182.895"
height="1367.9473"
rx="0"
style="fill:#dfdfdf;stroke:#000000;stroke-width:6.0930233;stroke-linecap:butt;stroke-linejoin:miter"
id="rect150-8" />
<text
xml:space="preserve"
x="4341.8867"
y="5887.8105"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-5-9"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-5-6">0</tspan></text>
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-1,1,0,3712.0549,5414.9455)"
id="g306-4">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308-1"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="3676.9697"
y="4404.876"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-8"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46392822px;stroke-width:0"
id="tspan3503-3">P4 table</tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
x="3090.0466"
y="4500.9346"
width="1181"
height="471.99997"
rx="0"
id="rect162-5-4" />
<text
xml:space="preserve"
x="3611.0537"
y="4780.9897"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-1"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46392822px;stroke-width:0"
id="tspan3495-6-41">P4 entry</tspan></text>
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-0.99999992,0.9999999,0,2296.6633,6284.4847)"
id="g306-2-3" />
<text
xml:space="preserve"
x="4405.4561"
y="4997.4966"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-8">511</tspan></text>
<ellipse
ry="46.999996"
rx="46.999992"
cx="4117.4102"
cy="4723.3125"
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="circle172-4-3" />
<text
xml:space="preserve"
x="3674.7217"
y="4178.6655"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-8-5"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46394348px;stroke-width:0"
id="tspan3503-3-0">active</tspan></text>
<rect
x="5370.3823"
y="4498.0884"
width="1182.895"
height="1367.9473"
rx="0"
style="fill:#dfdfbe;fill-opacity:1;stroke:#000000;stroke-width:6.0930233;stroke-linecap:butt;stroke-linejoin:miter"
id="rect150-8-9" />
<text
xml:space="preserve"
x="6622.1631"
y="5887.9717"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-5-9-3"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-5-6-5">0</tspan></text>
<g
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
transform="matrix(0,-1,1,0,5992.3314,5415.1068)"
id="g306-4-9">
<text
xml:space="preserve"
x="0"
y="0"
font-style="normal"
font-weight="normal"
font-size="404"
id="text308-1-8"
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
</g>
<text
xml:space="preserve"
x="5957.2456"
y="4405.0371"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-8-0"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46394348px;stroke-width:0"
id="tspan3503-3-2">P4 table</tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
x="5370.3228"
y="4501.0957"
width="1181"
height="472"
rx="0"
id="rect162-5-4-4" />
<text
xml:space="preserve"
x="5891.3296"
y="4781.1509"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-1-6"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46394348px;stroke-width:0"
id="tspan3495-6-41-6">P4 entry</tspan></text>
<text
xml:space="preserve"
x="6685.7324"
y="4997.6577"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-2-1-6-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
sodipodi:linespacing="125%"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
id="tspan3503-48-5-8-1">511</tspan></text>
<polygon
points="3150,6566 3150,6661 3150,6661 3308,6614 "
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="polygon232-2-7-7-1-1"
transform="translate(2076.0084,-749.88898)" />
<ellipse
ry="46.999996"
rx="46.999992"
cx="6397.6865"
cy="4723.4736"
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="circle172-4-3-6" />
<text
xml:space="preserve"
x="5954.9976"
y="4178.8267"
font-style="normal"
font-weight="normal"
font-size="152"
id="text320-8-5-9"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46394348px;stroke-width:0"
id="tspan3503-3-0-2">inactive</tspan></text>
<rect
x="1019.4597"
y="4821.9668"
width="1487.6132"
height="469.84174"
rx="0"
style="fill:#dfdfdf;stroke:#000000;stroke-width:4.0044775;stroke-linecap:butt;stroke-linejoin:miter"
id="rect150-8-0" />
<text
xml:space="preserve"
x="1642.8574"
y="5136.835"
font-style="normal"
font-weight="normal"
font-size="152"
id="text314-5-1-4"
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
style="font-size:200.46394348px;stroke-width:0"
id="tspan3495-6-41-8">CR3 register</tspan></text>
<ellipse
ry="46.999996"
rx="46.999992"
cx="2349.6458"
cy="5076.04"
style="fill:#969696;fill-opacity:1;stroke:#969696;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="circle172-4-3-2" />
<polygon
points="3150,6566 3150,6661 3150,6661 3308,6614 "
style="fill:#969696;fill-opacity:1;stroke:#969696;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="polygon232-2-7-7-1-1-7"
transform="translate(-214.18472,-748.49788)" />
<path
style="fill:none;fill-rule:evenodd;stroke:#969696;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2349.6459,5073.646 359.3939,0 0,792.4508 255.3589,0"
id="path4612"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 4118.0344,4723.1182 672.1066,0 0,1137.6237 494.8846,0"
id="path4614"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 6395.1719,4726.5039 441.3835,0 0,1357.6997 -1725.4081,0 0,-199.7613 127.0649,0"
id="path4620"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -1,740 +0,0 @@
+++
title = "Kernel Heap"
weight = 8
path = "kernel-heap"
aliases = ["kernel-heap.html"]
date = 2016-04-11
template = "edition-1/page.html"
[extra]
updated = "2017-11-19"
+++
In the previous posts we created a [frame allocator] and a [page table module]. Now we are ready to create a kernel heap and a memory allocator. Thus, we will unlock `Box`, `Vec`, `BTreeMap`, and the rest of the [alloc] crate.
[frame allocator]: @/edition-1/posts/05-allocating-frames/index.md
[page table module]: @/edition-1/posts/06-page-tables/index.md
[alloc]: https://doc.rust-lang.org/nightly/alloc/index.html
<!-- more -->
As always, you can find the complete source code on [GitHub]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
[GitHub]: https://github.com/phil-opp/blog_os/tree/first_edition_post_8
[issues]: https://github.com/phil-opp/blog_os/issues
## Introduction
The _heap_ is the memory area for long-lived allocations. The programmer can access it by using types like [Box][Box rustbyexample] or [Vec]. Behind the scenes, the compiler manages that memory by inserting calls to some memory allocator. By default, Rust links to the [jemalloc] allocator (for binaries) or the system allocator (for libraries). However, both rely on [system calls] such as [sbrk] and are thus unusable in our kernel. So we need to create and link our own allocator.
[Box rustbyexample]: https://doc.rust-lang.org/rust-by-example/std/box.html
[Vec]: https://doc.rust-lang.org/book/vectors.html
[jemalloc]: http://jemalloc.net/
[system calls]: https://en.wikipedia.org/wiki/System_call
[sbrk]: https://en.wikipedia.org/wiki/Sbrk
A good allocator is fast and reliable. It also effectively utilizes the available memory and keeps [fragmentation] low. Furthermore, it works well for concurrent applications and scales to any number of processors. It even optimizes the memory layout with respect to the CPU caches to improve [cache locality] and avoid [false sharing].
[cache locality]: https://www.geeksforgeeks.org/locality-of-reference-and-cache-operation-in-cache-memory/
[fragmentation]: https://en.wikipedia.org/wiki/Fragmentation_(computing)
[false sharing]: https://mechanical-sympathy.blogspot.de/2011/07/false-sharing.html
These requirements make good allocators pretty complex. For example, [jemalloc] has over 30.000 lines of code. This complexity is out of scope for our kernel, so we will create a much simpler allocator. Nevertheless, it should suffice for the foreseeable future, since we'll allocate only when it's absolutely necessary.
## The Allocator Interface
The allocator interface in Rust is defined through the [`Alloc` trait], which looks like this:
[`Alloc` trait]: https://doc.rust-lang.org/1.20.0/alloc/allocator/trait.Alloc.html
```rust
pub unsafe trait Alloc {
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>;
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout);
[] // about 13 methods with default implementations
}
```
The `alloc` method should allocate a memory block with the size and alignment given through `Layout` parameter. The `deallocate` method should free such memory blocks again. Both methods are `unsafe`, as is the trait itself. This has different reasons:
- Implementing the `Alloc` trait is unsafe, because the implementation must satisfy a set of contracts. Among other things, pointers returned by `alloc` must point to valid memory and adhere to the `Layout` requirements.
- Calling `alloc` is unsafe because the caller must ensure that the passed layout does not have size zero. I think this is because of compatibility reasons with existing C-allocators, where zero-sized allocations are undefined behavior.
- Calling `dealloc` is unsafe because the caller must guarantee that the passed parameters adhere to the contract. For example, `ptr` must denote a valid memory block allocated via this allocator.
To set the system allocator, the `global_allocator` attribute can be added to a `static` that implements `Alloc` for a shared reference of itself. For example:
```rust
#[global_allocator]
static MY_ALLOCATOR: MyAllocator = MyAllocator {...};
impl<'a> Alloc for &'a MyAllocator {
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {...}
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {...}
}
```
Note that `Alloc` needs to be implemented for `&MyAllocator`, not for `MyAllocator`. The reason is that the `alloc` and `dealloc` methods require mutable `self` references, but there's no way to get such a reference safely from a `static`. By requiring implementations for `&MyAllocator`, the global allocator interface avoids this problem and pushes the burden of synchronization onto the user.
## Including the alloc crate
The `Alloc` trait is part of the `alloc` crate, which like `core` is a subset of Rust's standard library. Apart from the trait, the crate also contains the standard types that require allocations such as `Box`, `Vec` and `Arc`. We can include it through a simple `extern crate`:
```rust
// in src/lib.rs
#![feature(alloc)] // the alloc crate is still unstable
[...]
#[macro_use]
extern crate alloc;
```
We don't need to add anything to our Cargo.toml, since the `alloc` crate is part of the standard library and shipped with the Rust compiler. The `alloc` crate provides the [format!] and [vec!] macros, so we use `#[macro_use]` to import them.
[format!]: https://doc.rust-lang.org/1.10.0/collections/macro.format!.html
[vec!]: https://doc.rust-lang.org/1.10.0/collections/macro.vec!.html
When we try to compile our crate now, the following error occurs:
```
error[E0463]: can't find crate for `alloc`
--> src/lib.rs:10:1
|
16 | extern crate alloc;
| ^^^^^^^^^^^^^^^^^^^ can't find crate
```
The problem is that [`xargo`] only cross compiles `libcore` by default. To also cross compile the `alloc` crate, we need to create a file named `Xargo.toml` in our project root (right next to the `Cargo.toml`) with the following content:
[`xargo`]: https://github.com/japaric/xargo
```toml
[target.x86_64-blog_os.dependencies]
alloc = {}
```
This instructs `xargo` that we also need `alloc`. It still doesn't compile, since we need to define a global allocator in order to use the `alloc` crate:
```
error: no #[default_lib_allocator] found but one is required; is libstd not linked?
```
## A Bump Allocator
For our first allocator, we start simple. We create a `memory::heap_allocator` module containing a so-called _bump allocator_:
```rust
// in src/memory/mod.rs
mod heap_allocator;
// in src/memory/heap_allocator.rs
use alloc::heap::{Alloc, AllocErr, Layout};
/// A simple allocator that allocates memory linearly and ignores freed memory.
#[derive(Debug)]
pub struct BumpAllocator {
heap_start: usize,
heap_end: usize,
next: usize,
}
impl BumpAllocator {
pub const fn new(heap_start: usize, heap_end: usize) -> Self {
Self { heap_start, heap_end, next: heap_start }
}
}
unsafe impl Alloc for BumpAllocator {
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
let alloc_start = align_up(self.next, layout.align());
let alloc_end = alloc_start.saturating_add(layout.size());
if alloc_end <= self.heap_end {
self.next = alloc_end;
Ok(alloc_start as *mut u8)
} else {
Err(AllocErr::Exhausted{ request: layout })
}
}
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
// do nothing, leak memory
}
}
```
We also need to add `#![feature(allocator_api)]` to our `lib.rs`, since the allocator API is still unstable.
The `heap_start` and `heap_end` fields contain the start and end address of our kernel heap. The `next` field contains the next free address and is increased after every allocation. To `allocate` a memory block we align the `next` address using the `align_up` function (described below). Then we add up the desired `size` and make sure that we don't exceed the end of the heap. We use a saturating add so that the `alloc_end` cannot overflow, which could lead to an invalid allocation. If everything goes well, we update the `next` address and return a pointer to the start address of the allocation. Else, we return `None`.
### Alignment
In order to simplify alignment, we add `align_down` and `align_up` functions:
``` rust
/// Align downwards. Returns the greatest x with alignment `align`
/// so that x <= addr. The alignment must be a power of 2.
pub fn align_down(addr: usize, align: usize) -> usize {
if align.is_power_of_two() {
addr & !(align - 1)
} else if align == 0 {
addr
} else {
panic!("`align` must be a power of 2");
}
}
/// Align upwards. Returns the smallest x with alignment `align`
/// so that x >= addr. The alignment must be a power of 2.
pub fn align_up(addr: usize, align: usize) -> usize {
align_down(addr + align - 1, align)
}
```
Let's start with `align_down`: If the alignment is a valid power of two (i.e. in `{1,2,4,8,…}`), we use some bitwise operations to return the aligned address. It works because every power of two has exactly one bit set in its binary representation. For example, the numbers `{1,2,4,8,…}` are `{1,10,100,1000,…}` in binary. By subtracting 1 we get `{0,01,011,0111,…}`. These binary numbers have a `1` at exactly the positions that need to be zeroed in `addr`. For example, the last 3 bits need to be zeroed for a alignment of 8.
To align `addr`, we create a [bitmask] from `align-1`. We want a `0` at the position of each `1`, so we invert it using `!`. After that, the binary numbers look like this: `{…11111,…11110,…11100,…11000,…}`. Finally, we zero the correct bits using a binary `AND`.
[bitmask]: https://en.wikipedia.org/wiki/Mask_(computing)
Aligning upwards is simple now. We just increase `addr` by `align-1` and call `align_down`. We add `align-1` instead of `align` because we would otherwise waste `align` bytes for already aligned addresses.
### Reusing Freed Memory
The heap memory is limited, so we should reuse freed memory for new allocations. This sounds simple, but is not so easy in practice since allocations can live arbitrarily long (and can be freed in an arbitrary order). This means that we need some kind of data structure to keep track of which memory areas are free and which are in use. This data structure should be very optimized since it causes overheads in both space (i.e. it needs backing memory) and time (i.e. accessing and organizing it needs CPU cycles).
Our bump allocator only keeps track of the next free memory address, which doesn't suffice to keep track of freed memory areas. So our only choice is to ignore deallocations and leak the corresponding memory. Thus our allocator quickly runs out of memory in a real system, but it suffices for simple testing. Later in this post, we will introduce a better allocator that does not leak freed memory.
### Using it as System Allocator
Above we saw that we can use a static allocator as system allocator through the `global_allocator` attribute:
```rust
#[global_allocator]
static ALLOCATOR: MyAllocator = MyAllocator {...};
```
This requires an implementation of `Alloc` for `&MyAllocator`, i.e. a shared reference. If we try to add such an implementation for our bump allocator (`unsafe impl<'a> Alloc for &'a BumpAllocator`), we have a problem: Our `alloc` method requires updating the `next` field, which is not possible for a shared reference.
One solution could be to put the bump allocator behind a Mutex and wrap it into a new type, for which we can implement `Alloc` for a shared reference:
```rust
struct LockedBumpAllocator(Mutex<BumpAllocator>);
impl<'a> Alloc for &'a LockedBumpAllocator {
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
self.0.lock().alloc(layout)
}
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
self.0.lock().dealloc(ptr, layout)
}
}
```
However, there is a more interesting solution for our bump allocator that avoids locking altogether. The idea is to exploit that we only need to update a single `usize` field byusing an `AtomicUsize` type. This type uses special synchronized hardware instructions to ensure data race freedom without requiring locks.
#### A lock-free Bump Allocator
A lock-free implementation looks like this:
```rust
use core::sync::atomic::{AtomicUsize, Ordering};
/// A simple allocator that allocates memory linearly and ignores freed memory.
#[derive(Debug)]
pub struct BumpAllocator {
heap_start: usize,
heap_end: usize,
next: AtomicUsize,
}
impl BumpAllocator {
pub const fn new(heap_start: usize, heap_end: usize) -> Self {
// NOTE: requires adding #![feature(const_atomic_usize_new)] to lib.rs
Self { heap_start, heap_end, next: AtomicUsize::new(heap_start) }
}
}
unsafe impl<'a> Alloc for &'a BumpAllocator {
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
loop {
// load current state of the `next` field
let current_next = self.next.load(Ordering::Relaxed);
let alloc_start = align_up(current_next, layout.align());
let alloc_end = alloc_start.saturating_add(layout.size());
if alloc_end <= self.heap_end {
// update the `next` pointer if it still has the value `current_next`
let next_now = self.next.compare_and_swap(current_next, alloc_end,
Ordering::Relaxed);
if next_now == current_next {
// next address was successfully updated, allocation succeeded
return Ok(alloc_start as *mut u8);
}
} else {
return Err(AllocErr::Exhausted{ request: layout })
}
}
}
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
// do nothing, leak memory
}
}
```
The implementation is a bit more complicated now. First, there is now a `loop` around the whole method body, since we might need multiple tries until we succeed (e.g. if multiple threads try to allocate at the same time). Also, the loads operation is an explicit method call now, i.e. `self.next.load(Ordering::Relaxed)` instead of just `self.next`. The ordering parameter makes it possible to restrict the automatic instruction reordering performed by both the compiler and the CPU itself. For example, it is used when implementing locks to ensure that no write to the locked variable happens before the lock is acquired. We don't have such requirements, so we use the less restrictive `Relaxed` ordering.
The heart of this lock-free method is the `compare_and_swap` call that updates the `next` address:
```rust
...
let next_now = self.next.compare_and_swap(current_next, alloc_end,
Ordering::Relaxed);
if next_now == current_next {
// next address was successfully updated, allocation succeeded
return Ok(alloc_start as *mut u8);
}
...
```
Compare-and-swap is a special CPU instruction that updates a variable with a given value if it still contains the value we expect. If it doesn't, it means that another thread updated the value simultaneously, so we need to try again. The important feature is that this happens in a single uninteruptible operation (thus the name `atomic`), so no partial updates or intermediate states are possible.
In detail, `compare_and_swap` works by comparing `next` with the first argument and, in case they're equal, updates `next` with the second parameter (the previous value is returned). To find out whether a switch happened, we check the returned previous value of `next`. If it is equal to the first parameter, the values were swapped. Otherwise, we try again in the next loop iteration.
#### Setting the Global Allocator
Now we can define a static bump allocator, that we can set as system allocator:
```rust
pub const HEAP_START: usize = 0o_000_001_000_000_0000;
pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB
#[global_allocator]
static HEAP_ALLOCATOR: BumpAllocator = BumpAllocator::new(HEAP_START,
HEAP_START + HEAP_SIZE);
```
We use `0o_000_001_000_000_0000` as heap start address, which is the address starting at the second `P3` entry. It doesn't really matter which address we choose here as long as it's unused. We use a heap size of 100 KiB, which should be large enough for the near future.
Putting the above in the `memory::heap_allocator` module would make most sense, but unfortunately there is currently a [weird bug][global allocator bug] in the global allocator implementation that requires putting the global allocator in the root module. I hope it's fixed soon, but until then we need to put the above lines in `src/lib.rs`. For that, we need to make the `memory::heap_allocator` module public and add an import for `BumpAllocator`. We also need to add the `#![feature(global_allocator)]` at the top of our `lib.rs`, since the `global_allocator` attribute is still unstable.
[global allocator bug]: https://github.com/rust-lang/rust/issues/44113
That's it! We have successfully created and linked a custom system allocator. Now we're ready to test it.
### Testing
We should be able to allocate memory on the heap now. Let's try it in our `rust_main`:
```rust
// in rust_main in src/lib.rs
use alloc::boxed::Box;
let heap_test = Box::new(42);
```
When we run it, a triple fault occurs and causes permanent rebooting. Let's try debug it using QEMU and objdump as described [in the previous post][qemu debugging]:
[qemu debugging]: @/edition-1/posts/07-remap-the-kernel/index.md#debugging
```
> qemu-system-x86_64 -d int -no-reboot -cdrom build/os-x86_64.iso
check_exception old: 0xffffffff new 0xe
0: v=0e e=0002 i=0 cpl=0 IP=0008:0000000000102860 pc=0000000000102860
SP=0010:0000000000116af0 CR2=0000000040000000
```
Aha! It's a [page fault] \(`v=0e`) and was caused by the code at `0x102860`. The code tried to write (`e=0002`) to address `0x40000000`. This address is `0o_000_001_000_000_0000` in octal, which is the `HEAP_START` address defined above. Of course it page-faults: We have forgotten to map the heap memory to some physical memory.
[page fault]: https://wiki.osdev.org/Exceptions#Page_Fault
### Some Refactoring
In order to map the heap cleanly, we do a bit of refactoring first. We move all memory initialization from our `rust_main` to a new `memory::init` function. Now our `rust_main` looks like this:
```rust
// in src/lib.rs
#[no_mangle]
pub extern "C" fn rust_main(multiboot_information_address: usize) {
// ATTENTION: we have a very small stack and no guard page
vga_buffer::clear_screen();
println!("Hello World{}", "!");
let boot_info = unsafe {
multiboot2::load(multiboot_information_address)
};
enable_nxe_bit();
enable_write_protect_bit();
// set up guard page and map the heap pages
memory::init(boot_info);
use alloc::boxed::Box;
let heap_test = Box::new(42);
println!("It did not crash!");
loop {}
}
```
The `memory::init` function looks like this:
```rust
// in src/memory/mod.rs
use multiboot2::BootInformation;
pub fn init(boot_info: &BootInformation) {
let memory_map_tag = boot_info.memory_map_tag().expect(
"Memory map tag required");
let elf_sections_tag = boot_info.elf_sections_tag().expect(
"Elf sections tag required");
let kernel_start = elf_sections_tag.sections()
.filter(|s| s.is_allocated()).map(|s| s.addr).min().unwrap();
let kernel_end = elf_sections_tag.sections()
.filter(|s| s.is_allocated()).map(|s| s.addr + s.size).max()
.unwrap();
println!("kernel start: {:#x}, kernel end: {:#x}",
kernel_start,
kernel_end);
println!("multiboot start: {:#x}, multiboot end: {:#x}",
boot_info.start_address(),
boot_info.end_address());
let mut frame_allocator = AreaFrameAllocator::new(
kernel_start as usize, kernel_end as usize,
boot_info.start_address(), boot_info.end_address(),
memory_map_tag.memory_areas());
paging::remap_the_kernel(&mut frame_allocator, boot_info);
}
```
We've just moved the code to a new function. However, we've sneaked some improvements in:
- An additional `.filter(|s| s.is_allocated())` in the calculation of `kernel_start` and `kernel_end`. This ignores all sections that aren't loaded to memory (such as debug sections). Thus, the kernel end address is no longer artificially increased by such sections.
- We use the `start_address()` and `end_address()` methods of `boot_info` instead of calculating the addresses manually.
- We use the alternate `{:#x}` form when printing kernel/multiboot addresses. Before, we used `0x{:x}`, which leads to the same result. For a complete list of these “alternate” formatting forms, check out the [std::fmt documentation].
[std::fmt documentation]: https://doc.rust-lang.org/nightly/std/fmt/index.html#sign0
### Safety
It is important that the `memory::init` function is called only once, because it creates a new frame allocator based on kernel and multiboot start/end. When we call it a second time, a new frame allocator is created that reassigns the same frames, even if they are already in use.
In the second call it would use an identical frame allocator to remap the kernel. The `remap_the_kernel` function would request a frame from the frame allocator to create a new page table. But the returned frame is already in use, since we used it to create our current page table in the first call. In order to initialize the new table, the function zeroes it. This is the point where everything breaks, since we zero our current page table. The CPU is unable to read the next instruction and throws a page fault.
So we need to ensure that `memory::init` can be only called once. We could mark it as `unsafe`, which would bring it in line with Rust's memory safety rules. However, that would just push the unsafety to the caller. The caller can still accidentally call the function twice, the only difference is that the mistake needs to happen inside `unsafe` blocks.
A better solution is to insert a check at the function's beginning, that panics if the function is called a second time. This approach has a small runtime cost, but we only call it once, so it's negligible. And we avoid two `unsafe` blocks (one at the calling site and one at the function itself), which is always good.
In order to make such checks easy, I created a small crate named [once]. To add it, we run `cargo add once` and add the following to our `src/lib.rs`:
[once]: https://crates.io/crates/once
```rust
// in src/lib.rs
#[macro_use]
extern crate once;
```
The crate provides an [assert_has_not_been_called!] macro (sorry for the long name :D). We can use it to fix the safety problem easily:
[assert_has_not_been_called!]: https://docs.rs/once/0.3.2/once/macro.assert_has_not_been_called!.html
``` rust
// in src/memory/mod.rs
pub fn init(boot_info: &BootInformation) {
assert_has_not_been_called!("memory::init must be called only once");
let memory_map_tag = ...
...
}
```
That's it. Now our `memory::init` function can only be called once. The macro works by creating a static [AtomicBool] named `CALLED`, which is initialized to `false`. When the macro is invoked, it checks the value of `CALLED` and sets it to `true`. If the value was already `true` before, the macro panics.
[AtomicBool]: https://doc.rust-lang.org/nightly/core/sync/atomic/struct.AtomicBool.html
### Mapping the Heap
Now we're ready to map the heap pages. In order to do it, we need access to the `ActivePageTable` or `Mapper` instance (see the [page table] and [kernel remapping] posts). For that we return it from the `paging::remap_the_kernel` function:
[page table]: @/edition-1/posts/06-page-tables/index.md
[kernel remapping]: @/edition-1/posts/07-remap-the-kernel/index.md
```rust
// in src/memory/paging/mod.rs
pub fn remap_the_kernel<A>(allocator: &mut A, boot_info: &BootInformation)
-> ActivePageTable // new
where A: FrameAllocator
{
...
println!("guard page at {:#x}", old_p4_page.start_address());
active_table // new
}
```
Now we have full page table access in the `memory::init` function. This allows us to map the heap pages to physical frames:
```rust
// in src/memory/mod.rs
pub fn init(boot_info: &BootInformation) {
...
let mut frame_allocator = ...;
// below is the new part
let mut active_table = paging::remap_the_kernel(&mut frame_allocator,
boot_info);
use self::paging::Page;
use {HEAP_START, HEAP_SIZE};
let heap_start_page = Page::containing_address(HEAP_START);
let heap_end_page = Page::containing_address(HEAP_START + HEAP_SIZE-1);
for page in Page::range_inclusive(heap_start_page, heap_end_page) {
active_table.map(page, paging::WRITABLE, &mut frame_allocator);
}
}
```
The `Page::range_inclusive` function is just a copy of the `Frame::range_inclusive` function:
```rust
// in src/memory/paging/mod.rs
#[derive(…, PartialEq, Eq, PartialOrd, Ord)]
pub struct Page {...}
impl Page {
...
pub fn range_inclusive(start: Page, end: Page) -> PageIter {
PageIter {
start: start,
end: end,
}
}
}
pub struct PageIter {
start: Page,
end: Page,
}
impl Iterator for PageIter {
type Item = Page;
fn next(&mut self) -> Option<Page> {
if self.start <= self.end {
let page = self.start;
self.start.number += 1;
Some(page)
} else {
None
}
}
}
```
Now we map the whole heap to physical pages. This needs some time and might introduce a noticeable delay when we increase the heap size in the future. Another drawback is that we consume a large amount of physical frames even though we might not need the whole heap space. We will fix these problems in a future post by mapping the pages lazily.
### It works!
Now `Box` and `Vec` should work. For example:
```rust
// in rust_main in src/lib.rs
use alloc::boxed::Box;
let mut heap_test = Box::new(42);
*heap_test -= 15;
let heap_test2 = Box::new("hello");
println!("{:?} {:?}", heap_test, heap_test2);
let mut vec_test = vec![1,2,3,4,5,6,7];
vec_test[3] = 42;
for i in &vec_test {
print!("{} ", i);
}
```
We can also use all other types of the `alloc` crate, including:
- the reference counted pointers [Rc] and [Arc]
- the owned string type [String] and the [format!] macro
- [Linked List]
- the growable ring buffer [VecDeque]
- [BinaryHeap]
- [BTreeMap] and [BTreeSet]
[Rc]: https://doc.rust-lang.org/1.10.0/alloc/rc/
[Arc]: https://doc.rust-lang.org/1.10.0/alloc/arc/
[String]: https://doc.rust-lang.org/1.10.0/collections/string/struct.String.html
[Linked List]: https://doc.rust-lang.org/1.10.0/collections/linked_list/struct.LinkedList.html
[VecDeque]: https://doc.rust-lang.org/1.10.0/collections/vec_deque/struct.VecDeque.html
[BinaryHeap]: https://doc.rust-lang.org/1.10.0/collections/binary_heap/struct.BinaryHeap.html
[BTreeMap]: https://doc.rust-lang.org/1.10.0/collections/btree_map/struct.BTreeMap.html
[BTreeSet]: https://doc.rust-lang.org/1.10.0/collections/btree_set/struct.BTreeSet.html
## A better Allocator
Right now, we leak every freed memory block. Thus, we run out of memory quickly, for example, by creating a new `String` in each iteration of a loop:
```rust
// in rust_main in src/lib.rs
for i in 0..10000 {
format!("Some String");
}
```
To fix this, we need to create an allocator that keeps track of freed memory blocks and reuses them if possible. This introduces some challenges:
- We need to keep track of a possibly unlimited number of freed blocks. For example, an application could allocate `n` one-byte sized blocks and free every second block, which creates `n/2` freed blocks. We can't rely on any upper bound of freed block since `n` could be arbitrarily large.
- We can't use any of the collections from above, since they rely on allocations themselves. (It might be possible as soon as [RFC #1398] is [implemented][#32838], which allows user-defined allocators for specific collection instances.)
- We need to merge adjacent freed blocks if possible. Otherwise, the freed memory is no longer usable for large allocations. We will discuss this point in more detail below.
- Our allocator should search the set of freed blocks quickly and keep fragmentation low.
[RFC #1398]: https://github.com/rust-lang/rfcs/blob/master/text/1398-kinds-of-allocators.md
[#32838]: https://github.com/rust-lang/rust/issues/32838
### Creating a List of freed Blocks
Where do we store the information about an unlimited number of freed blocks? We can't use any fixed size data structure since it could always be too small for some allocation sequences. So we need some kind of dynamically growing set.
One possible solution could be to use an array-like data structure that starts at some unused virtual address. If the array becomes full, we increase its size and map new physical frames as backing storage. This approach would require a large part of the virtual address space since the array could grow significantly. We would need to create a custom implementation of a growable array and manipulate the page tables when deallocating. It would also consume a possibly large number of physical frames as backing storage.
We will choose another solution with different tradoffs. It's not clearly “better” than the approach above and has significant disadvantages itself. However, it has one big advantage: It does not need any additional physical or virtual memory at all. This makes it less complex since we don't need to manipulate any page tables. The idea is the following:
A freed memory block is not used anymore and no one needs the stored information. It is still mapped to a virtual address and backed by a physical page. So we just store the information about the freed block _in the block itself_. We keep a pointer to the first block and store a pointer to the next block in each block. Thus, we create a single linked list:
![Linked List Allocator](overview.svg)
In the following, we call a freed block a _hole_. Each hole stores its size and a pointer to the next hole. If a hole is larger than needed, we leave the remaining memory unused. By storing a pointer to the first hole, we are able to traverse the complete list.
#### Initialization
When the heap is created, all of its memory is unused. Thus, it forms a single large hole:
![Heap Initialization](initialization.svg)
The optional pointer to the next hole is set to `None`.
#### Allocation
In order to allocate a block of memory, we need to find a hole that satisfies the size and alignment requirements. If the found hole is larger than required, we split it into two smaller holes. For example, when we allocate a 24 byte block right after initialization, we split the single hole into a hole of size 24 and a hole with the remaining size:
![split hole](split-hole.svg)
Then we use the new 24 byte hole to perform the allocation:
![24 bytes allocated](allocate.svg)
To find a suitable hole, we can use several search strategies:
- **best fit**: Search the whole list and choose the _smallest_ hole that satisfies the requirements.
- **worst fit**: Search the whole list and choose the _largest_ hole that satisfies the requirements.
- **first fit**: Search the list from the beginning and choose the _first_ hole that satisfies the requirements.
Each strategy has its advantages and disadvantages. Best fit uses the smallest hole possible and leaves larger holes for large allocations. But splitting the smallest hole might create a tiny hole, which is too small for most allocations. In contrast, the worst fit strategy always chooses the largest hole. Thus, it does not create tiny holes, but it consumes the large block, which might be required for large allocations.
For our use case, the best fit strategy is better than worst fit. The reason is that we have a minimal hole size of 16 bytes, since each hole needs to be able to store a size (8 bytes) and a pointer to the next hole (8 bytes). Thus, even the best fit strategy leads to holes of usable size. Furthermore, we will need to allocate very large blocks occasionally (e.g. for [DMA] buffers).
[DMA]: https://en.wikipedia.org/wiki/Direct_memory_access
However, both best fit and worst fit have a significant problem: They need to scan the whole list for each allocation in order to find the optimal block. This leads to long allocation times if the list is long. The first fit strategy does not have this problem, as it returns as soon as it finds a suitable hole. It is fairly fast for small allocations and might only need to scan the whole list for large allocations.
#### Deallocation
To deallocate a block of memory, we can just insert its corresponding hole somewhere into the list. However, we need to merge adjacent holes. Otherwise, we are unable to reuse the freed memory for larger allocations. For example:
![deallocate memory, which leads to adjacent holes](deallocate.svg)
In order to use these adjacent holes for a large allocation, we need to merge them to a single large hole first:
![merge adjacent holes and allocate large block](merge-holes-and-allocate.svg)
The easiest way to ensure that adjacent holes are always merged, is to keep the hole list sorted by address. Thus, we only need to check the predecessor and the successor in the list when we free a memory block. If they are adjacent to the freed block, we merge the corresponding holes. Else, we insert the freed block as a new hole at the correct position.
### Implementation
The detailed implementation would go beyond the scope of this post, since it contains several hidden difficulties. For example:
- Several merge cases: Merge with the previous hole, merge with the next hole, merge with both holes.
- We need to satisfy the alignment requirements, which requires additional splitting logic.
- The minimal hole size of 16 bytes: We must not create smaller holes when splitting a hole.
I created the [linked_list_allocator] crate to handle all of these cases. It consists of a [Heap struct] that provides an `allocate_first_fit` and a `deallocate` method. It also contains a [LockedHeap] type that wraps `Heap` into spinlock so that it's usable as a static system allocator. If you are interested in the implementation details, check out the [source code][linked_list_allocator source].
[linked_list_allocator]: https://docs.rs/crate/linked_list_allocator/0.4.1
[Heap struct]: https://docs.rs/linked_list_allocator/0.4.1/linked_list_allocator/struct.Heap.html
[LockedHeap]: https://docs.rs/linked_list_allocator/0.4.1/linked_list_allocator/struct.LockedHeap.html
[linked_list_allocator source]: https://github.com/phil-opp/linked-list-allocator
We need to add the extern crate to our `Cargo.toml` and our `lib.rs`:
``` bash
> cargo add linked_list_allocator
```
```rust
// in src/lib.rs
extern crate linked_list_allocator;
```
Now we can change our global allocator:
```rust
use linked_list_allocator::LockedHeap;
#[global_allocator]
static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty();
```
We can't initialize the linked list allocator statically, since it needs to initialize the first hole (like described [above](#initialization)). This can't be done at compile time, so the function can't be a `const` function. Therefore we can only create an empty heap and initialize it later at runtime. For that, we add the following lines to our `rust_main` function:
```rust
// in src/lib.rs
#[no_mangle]
pub extern "C" fn rust_main(multiboot_information_address: usize) {
[…]
// set up guard page and map the heap pages
memory::init(boot_info);
// initialize the heap allocator
unsafe {
HEAP_ALLOCATOR.lock().init(HEAP_START, HEAP_START + HEAP_SIZE);
}
[…]
}
```
It is important that we initialize the heap _after_ mapping the heap pages, since the init function writes to the heap memory (the first hole).
Our kernel uses the new allocator now, so we can deallocate memory without leaking it. The example from above should work now without causing an OOM situation:
```rust
// in rust_main in src/lib.rs
for i in 0..10000 {
format!("Some String");
}
```
### Performance
The linked list based approach has some performance problems. Each allocation or deallocation might need to scan the complete list of holes in the worst case. However, I think it's good enough for now, since our heap will stay relatively small for the near future. When our allocator becomes a performance problem eventually, we can just replace it with a faster alternative.
## Summary
Now we're able to use heap storage in our kernel without leaking memory. This allows us to effectively process dynamic data such as user supplied strings in the future. We can also use `Rc` and `Arc` to create types with shared ownership. And we have access to various data structures such as `Vec` or `Linked List`, which will make our lives much easier. We even have some well tested and optimized [binary heap] and [B-tree] implementations!
[binary heap]:https://en.wikipedia.org/wiki/Binary_heap
[B-tree]: https://en.wikipedia.org/wiki/B-tree
## What's next?
This post concludes the section about memory management for now. We will revisit this topic eventually, but now it's time to explore other topics. The upcoming posts will be about CPU exceptions and interrupts. We will catch all page, double, and triple faults and create a driver to read keyboard input. The [next post] starts by setting up a so-called _Interrupt Descriptor Table_.
[next post]: @/edition-1/posts/09-handling-exceptions/index.md

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.3 KiB

View File

@@ -1,473 +0,0 @@
+++
title = "Handling Exceptions"
weight = 9
path = "handling-exceptions"
aliases = ["handling-exceptions.html"]
date = 2017-03-26
template = "edition-1/page.html"
+++
In this post, we start exploring CPU exceptions. Exceptions occur in various erroneous situations, for example when accessing an invalid memory address or when dividing by zero. To catch them, we have to set up an _interrupt descriptor table_ that provides handler functions. At the end of this post, our kernel will be able to catch [breakpoint exceptions] and to resume normal execution afterwards.
[breakpoint exceptions]: https://wiki.osdev.org/Exceptions#Breakpoint
<!-- more -->
As always, the complete source code is available on [GitHub]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
[GitHub]: https://github.com/phil-opp/blog_os/tree/first_edition_post_9
[issues]: https://github.com/phil-opp/blog_os/issues
## Exceptions
An exception signals that something is wrong with the current instruction. For example, the CPU issues an exception if the current instruction tries to divide by 0. When an exception occurs, the CPU interrupts its current work and immediately calls a specific exception handler function, depending on the exception type.
We've already seen several types of exceptions in our kernel:
- **Invalid Opcode**: This exception occurs when the current instruction is invalid. For example, this exception occurred when we tried to use SSE instructions before enabling SSE. Without SSE, the CPU didn't know the `movups` and `movaps` instructions, so it throws an exception when it stumbles over them.
- **Page Fault**: A page fault occurs on illegal memory accesses. For example, if the current instruction tries to read from an unmapped page or tries to write to a read-only page.
- **Double Fault**: When an exception occurs, the CPU tries to call the corresponding handler function. If another exception occurs _while calling the exception handler_, the CPU raises a double fault exception. This exception also occurs when there is no handler function registered for an exception.
- **Triple Fault**: If an exception occurs while the CPU tries to call the double fault handler function, it issues a fatal _triple fault_. We can't catch or handle a triple fault. Most processors react by resetting themselves and rebooting the operating system. This causes the bootloops we experienced in the previous posts.
For the full list of exceptions check out the [OSDev wiki][exceptions].
[exceptions]: https://wiki.osdev.org/Exceptions
### The Interrupt Descriptor Table
In order to catch and handle exceptions, we have to set up a so-called _Interrupt Descriptor Table_ (IDT). In this table we can specify a handler function for each CPU exception. The hardware uses this table directly, so we need to follow a predefined format. Each entry must have the following 16-byte structure:
Type| Name | Description
----|--------------------------|-----------------------------------
u16 | Function Pointer [0:15] | The lower bits of the pointer to the handler function.
u16 | GDT selector | Selector of a code segment in the GDT.
u16 | Options | (see below)
u16 | Function Pointer [16:31] | The middle bits of the pointer to the handler function.
u32 | Function Pointer [32:63] | The remaining bits of the pointer to the handler function.
u32 | Reserved |
The options field has the following format:
Bits | Name | Description
------|-----------------------------------|-----------------------------------
0-2 | Interrupt Stack Table Index | 0: Don't switch stacks, 1-7: Switch to the n-th stack in the Interrupt Stack Table when this handler is called.
3-7 | Reserved |
8 | 0: Interrupt Gate, 1: Trap Gate | If this bit is 0, interrupts are disabled when this handler is called.
9-11 | must be one |
12 | must be zero |
1314 | Descriptor Privilege Level (DPL) | The minimal privilege level required for calling this handler.
15 | Present |
Each exception has a predefined IDT index. For example the invalid opcode exception has table index 6 and the page fault exception has table index 14. Thus, the hardware can automatically load the corresponding IDT entry for each exception. The [Exception Table][exceptions] in the OSDev wiki shows the IDT indexes of all exceptions in the “Vector nr.” column.
When an exception occurs, the CPU roughly does the following:
1. Push some registers on the stack, including the instruction pointer and the [RFLAGS] register. (We will use these values later in this post.)
2. Read the corresponding entry from the Interrupt Descriptor Table (IDT). For example, the CPU reads the 14-th entry when a page fault occurs.
3. Check if the entry is present. Raise a double fault if not.
4. Disable interrupts if the entry is an interrupt gate (bit 40 not set).
5. Load the specified GDT selector into the CS segment.
6. Jump to the specified handler function.
[RFLAGS]: https://en.wikipedia.org/wiki/FLAGS_register
## An IDT Type
Instead of creating our own IDT type, we will use the [`Idt` struct] of the `x86_64` crate, which looks like this:
[`Idt` struct]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.Idt.html
``` rust
#[repr(C)]
pub struct Idt {
pub divide_by_zero: IdtEntry<HandlerFunc>,
pub debug: IdtEntry<HandlerFunc>,
pub non_maskable_interrupt: IdtEntry<HandlerFunc>,
pub breakpoint: IdtEntry<HandlerFunc>,
pub overflow: IdtEntry<HandlerFunc>,
pub bound_range_exceeded: IdtEntry<HandlerFunc>,
pub invalid_opcode: IdtEntry<HandlerFunc>,
pub device_not_available: IdtEntry<HandlerFunc>,
pub double_fault: IdtEntry<HandlerFuncWithErrCode>,
pub invalid_tss: IdtEntry<HandlerFuncWithErrCode>,
pub segment_not_present: IdtEntry<HandlerFuncWithErrCode>,
pub stack_segment_fault: IdtEntry<HandlerFuncWithErrCode>,
pub general_protection_fault: IdtEntry<HandlerFuncWithErrCode>,
pub page_fault: IdtEntry<PageFaultHandlerFunc>,
pub x87_floating_point: IdtEntry<HandlerFunc>,
pub alignment_check: IdtEntry<HandlerFuncWithErrCode>,
pub machine_check: IdtEntry<HandlerFunc>,
pub simd_floating_point: IdtEntry<HandlerFunc>,
pub virtualization: IdtEntry<HandlerFunc>,
pub security_exception: IdtEntry<HandlerFuncWithErrCode>,
pub interrupts: [IdtEntry<HandlerFunc>; 224],
// some fields omitted
}
```
The fields have the type [`IdtEntry<F>`], which is a struct that represents the fields of an IDT entry (see the table above). The type parameter `F` defines the expected handler function type. We see that some entries require a [`HandlerFunc`] and some entries require a [`HandlerFuncWithErrCode`]. The page fault even has its own special type: [`PageFaultHandlerFunc`].
[`IdtEntry<F>`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.IdtEntry.html
[`HandlerFunc`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/type.HandlerFunc.html
[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/type.HandlerFuncWithErrCode.html
[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/type.PageFaultHandlerFunc.html
Let's look at the `HandlerFunc` type first:
```rust
type HandlerFunc = extern "x86-interrupt" fn(_: &mut ExceptionStackFrame);
```
It's a [type alias] for an `extern "x86-interrupt" fn` type. The `extern` keyword defines a function with a [foreign calling convention] and is often used to communicate with C code (`extern "C" fn`). But what is the `x86-interrupt` calling convention?
[type alias]: https://doc.rust-lang.org/book/type-aliases.html
[foreign calling convention]: https://doc.rust-lang.org/1.30.0/book/first-edition/ffi.html#foreign-calling-conventions
## The Interrupt Calling Convention
Exceptions are quite similar to function calls: The CPU jumps to the first instruction of the called function and executes it. Afterwards, if the function is not diverging, the CPU jumps to the return address and continues the execution of the parent function.
However, there is a major difference between exceptions and function calls: A function call is invoked voluntary by a compiler inserted `call` instruction, while an exception might occur at _any_ instruction. In order to understand the consequences of this difference, we need to examine function calls in more detail.
[Calling conventions] specify the details of a function call. For example, they specify where function parameters are placed (e.g. in registers or on the stack) and how results are returned. On x86_64 Linux, the following rules apply for C functions (specified in the [System V ABI]):
[Calling conventions]: https://en.wikipedia.org/wiki/Calling_convention
[System V ABI]: https://refspecs.linuxbase.org/elf/gabi41.pdf
- the first six integer arguments are passed in registers `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
- additional arguments are passed on the stack
- results are returned in `rax` and `rdx`
Note that Rust does not follow the C ABI (in fact, [there isn't even a Rust ABI yet][rust abi]). So these rules apply only to functions declared as `extern "C" fn`.
[rust abi]: https://github.com/rust-lang/rfcs/issues/600
### Preserved and Scratch Registers
The calling convention divides the registers in two parts: _preserved_ and _scratch_ registers.
The values of _preserved_ registers must remain unchanged across function calls. So a called function (the _“callee”_) is only allowed to overwrite these registers if it restores their original values before returning. Therefore these registers are called _“callee-saved”_. A common pattern is to save these registers to the stack at the function's beginning and restore them just before returning.
In contrast, a called function is allowed to overwrite _scratch_ registers without restrictions. If the caller wants to preserve the value of a scratch register across a function call, it needs to backup and restore it before the function call (e.g. by pushing it to the stack). So the scratch registers are _caller-saved_.
On x86_64, the C calling convention specifies the following preserved and scratch registers:
preserved registers | scratch registers
---|---
`rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11`
_callee-saved_ | _caller-saved_
The compiler knows these rules, so it generates the code accordingly. For example, most functions begin with a `push rbp`, which backups `rbp` on the stack (because it's a callee-saved register).
### Preserving all Registers
In contrast to function calls, exceptions can occur on _any_ instruction. In most cases we don't even know at compile time if the generated code will cause an exception. For example, the compiler can't know if an instruction causes a stack overflow or a page fault.
Since we don't know when an exception occurs, we can't backup any registers before. This means that we can't use a calling convention that relies on caller-saved registers for exception handlers. Instead, we need a calling convention means that preserves _all registers_. The `x86-interrupt` calling convention is such a calling convention, so it guarantees that all register values are restored to their original values on function return.
### The Exception Stack Frame
On a normal function call (using the `call` instruction), the CPU pushes the return address before jumping to the target function. On function return (using the `ret` instruction), the CPU pops this return address and jumps to it. So the stack frame of a normal function call looks like this:
![function stack frame](function-stack-frame.svg)
For exception and interrupt handlers, however, pushing a return address would not suffice, since interrupt handlers often run in a different context (stack pointer, CPU flags, etc.). Instead, the CPU performs the following steps when an interrupt occurs:
1. **Aligning the stack pointer**: An interrupt can occur at any instructions, so the stack pointer can have any value, too. However, some CPU instructions (e.g. some SSE instructions) require that the stack pointer is aligned on a 16 byte boundary, therefore the CPU performs such an alignment right after the interrupt.
2. **Switching stacks** (in some cases): A stack switch occurs when the CPU privilege level changes, for example when a CPU exception occurs in an user mode program. It is also possible to configure stack switches for specific interrupts using the so-called _Interrupt Stack Table_ (described in the next post).
3. **Pushing the old stack pointer**: The CPU pushes the values of the stack pointer (`rsp`) and the stack segment (`ss`) registers at the time when the interrupt occurred (before the alignment). This makes it possible to restore the original stack pointer when returning from an interrupt handler.
4. **Pushing and updating the `RFLAGS` register**: The [`RFLAGS`] register contains various control and status bits. On interrupt entry, the CPU changes some bits and pushes the old value.
5. **Pushing the instruction pointer**: Before jumping to the interrupt handler function, the CPU pushes the instruction pointer (`rip`) and the code segment (`cs`). This is comparable to the return address push of a normal function call.
6. **Pushing an error code** (for some exceptions): For some specific exceptions such as page faults, the CPU pushes an error code, which describes the cause of the exception.
7. **Invoking the interrupt handler**: The CPU reads the address and the segment descriptor of the interrupt handler function from the corresponding field in the IDT. It then invokes this handler by loading the values into the `rip` and `cs` registers.
[`RFLAGS`]: https://en.wikipedia.org/wiki/FLAGS_register
So the _exception stack frame_ looks like this:
![exception stack frame](exception-stack-frame.svg)
In the `x86_64` crate, the exception stack frame is represented by the [`ExceptionStackFrame`] struct. It is passed to interrupt handlers as `&mut` and can be used to retrieve additional information about the exception's cause. The struct contains no error code field, since only some few exceptions push an error code. These exceptions use the separate [`HandlerFuncWithErrCode`] function type, which has an additional `error_code` argument.
[`ExceptionStackFrame`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.ExceptionStackFrame.html
### Behind the Scenes
The `x86-interrupt` calling convention is a powerful abstraction that hides almost all of the messy details of the exception handling process. However, sometimes it's useful to know what's happening behind the curtain. Here is a short overview of the things that the `x86-interrupt` calling convention takes care of:
- **Retrieving the arguments**: Most calling conventions expect that the arguments are passed in registers. This is not possible for exception handlers, since we must not overwrite any register values before backing them up on the stack. Instead, the `x86-interrupt` calling convention is aware that the arguments already lie on the stack at a specific offset.
- **Returning using `iretq`**: Since the exception stack frame completely differs from stack frames of normal function calls, we can't return from handlers functions through the normal `ret` instruction. Instead, the `iretq` instruction must be used.
- **Handling the error code**: The error code, which is pushed for some exceptions, makes things much more complex. It changes the stack alignment (see the next point) and needs to be popped off the stack before returning. The `x86-interrupt` calling convention handles all that complexity. However, it doesn't know which handler function is used for which exception, so it needs to deduce that information from the number of function arguments. That means that the programmer is still responsible to use the correct function type for each exception. Luckily, the `Idt` type defined by the `x86_64` crate ensures that the correct function types are used.
- **Aligning the stack**: There are some instructions (especially SSE instructions) that require a 16-byte stack alignment. The CPU ensures this alignment whenever an exception occurs, but for some exceptions it destroys it again later when it pushes an error code. The `x86-interrupt` calling convention takes care of this by realigning the stack in this case.
If you are interested in more details: We also have a series of posts that explains exception handling using [naked functions] linked [at the end of this post][too-much-magic].
[naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
[too-much-magic]: #too-much-magic
## Implementation
Now that we've understood the theory, it's time to handle CPU exceptions in our kernel. We start by creating a new `interrupts` module:
``` rust
// in src/lib.rs
...
mod interrupts;
...
```
In the new module, we create an `init` function, that creates a new `Idt`:
``` rust
// in src/interrupts.rs
use x86_64::structures::idt::Idt;
pub fn init() {
let mut idt = Idt::new();
}
```
Now we can add handler functions. We start by adding a handler for the [breakpoint exception]. The breakpoint exception is the perfect exception to test exception handling. Its only purpose is to temporary pause a program when the breakpoint instruction `int3` is executed.
[breakpoint exception]: https://wiki.osdev.org/Exceptions#Breakpoint
The breakpoint exception is commonly used in debuggers: When the user sets a breakpoint, the debugger overwrites the corresponding instruction with the `int3` instruction so that the CPU throws the breakpoint exception when it reaches that line. When the user wants to continue the program, the debugger replaces the `int3` instruction with the original instruction again and continues the program. For more details, see the ["_How debuggers work_"] series.
["_How debuggers work_"]: https://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints
For our use case, we don't need to overwrite any instructions (it wouldn't even be possible since we [set the page table flags] to read-only). Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program.
[set the page table flags]: @/edition-1/posts/07-remap-the-kernel/index.md#using-the-correct-flags
So let's create a simple `breakpoint_handler` function and add it to our IDT:
```rust
/// in src/interrupts.rs
use x86_64::structures::idt::ExceptionStackFrame;
pub fn init() {
let mut idt = Idt::new();
idt.breakpoint.set_handler_fn(breakpoint_handler);
}
extern "x86-interrupt" fn breakpoint_handler(
stack_frame: &mut ExceptionStackFrame)
{
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
}
```
Our handler just outputs a message and pretty-prints the exception stack frame.
When we try to compile it, the following error occurs:
```
error: x86-interrupt ABI is experimental and subject to change (see issue #40180)
--> src/interrupts.rs:8:1
|
8 | extern "x86-interrupt" fn breakpoint_handler(
| _^ starting here...
9 | | stack_frame: &mut ExceptionStackFrame)
10 | | {
11 | | println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
12 | | }
| |_^ ...ending here
|
= help: add #![feature(abi_x86_interrupt)] to the crate attributes to enable
```
This error occurs because the `x86-interrupt` calling convention is still unstable. To use it anyway, we have to explicitly enable it by adding `#![feature(abi_x86_interrupt)]` on the top of our `lib.rs`.
### Loading the IDT
In order that the CPU uses our new interrupt descriptor table, we need to load it using the [`lidt`] instruction. The `Idt` struct of the `x86_64` provides a [`load`][Idt::load] method function for that. Let's try to use it:
[`lidt`]: https://www.felixcloutier.com/x86/lgdt:lidt
[Idt::load]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.Idt.html#method.load
```rust
pub fn init() {
let mut idt = Idt::new();
idt.breakpoint.set_handler_fn(breakpoint_handler);
idt.load();
}
```
When we try to compile it now, the following error occurs:
```
error: `idt` does not live long enough
--> src/interrupts/mod.rs:43:5
|
43 | idt.load();
| ^^^ does not live long enough
44 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
```
So the `load` methods expects a `&'static self`, that is a reference that is valid for the complete runtime of the program. The reason is that the CPU will access this table on every interrupt until we load a different IDT. So using a shorter lifetime than `'static` could lead to use-after-free bugs.
In fact, this is exactly what happens here. Our `idt` is created on the stack, so it is only valid inside the `init` function. Afterwards the stack memory is reused for other functions, so the CPU would interpret random stack memory as IDT. Luckily, the `Idt::load` method encodes this lifetime requirement in its function definition, so that the Rust compiler is able to prevent this possible bug at compile time.
In order to fix this problem, we need to store our `idt` at a place where it has a `'static` lifetime. To achieve this, we could either allocate our IDT on the heap using `Box` and then convert it to a `'static` reference or we can store the IDT as a `static`. Let's try the latter:
```rust
static IDT: Idt = Idt::new();
pub fn init() {
IDT.breakpoint.set_handler_fn(breakpoint_handler);
IDT.load();
}
```
There are two problems with this. First, statics are immutable, so we can't modify the breakpoint entry from our `init` function. Second, the `Idt::new` function is not a [`const` function], so it can't be used to initialize a `static`. We could solve this problem by using a [`static mut`] of type `Option<Idt>`:
[`const` function]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
[`static mut`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
```rust
static mut IDT: Option<Idt> = None;
pub fn init() {
unsafe {
let IDT = Some(Idt::new());
let idt = IDT.as_mut_ref().unwrap();
idt.breakpoint.set_handler_fn(breakpoint_handler);
idt.load();
}
}
```
This variant compiles without errors but it's far from idiomatic. `static mut`s are very prone to data races, so we need an [`unsafe` block] on each access. Also, we need to explicitly `unwrap` the `IDT` on each use, since might be `None`.
[`unsafe` block]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#unsafe-superpowers
#### Lazy Statics to the Rescue
The one-time initialization of statics with non-const functions is a common problem in Rust. Fortunately, there already exists a good solution in a crate named [lazy_static]. This crate provides a `lazy_static!` macro that defines a lazily initialized `static`. Instead of computing its value at compile time, the `static` laziliy initializes itself when it's accessed the first time. Thus, the initialization happens at runtime so that arbitrarily complex initialization code is possible.
[lazy_static]: https://docs.rs/lazy_static/0.2.4/lazy_static/
Let's add the `lazy_static` crate to our project:
```rust
// in src/lib.rs
#[macro_use]
extern crate lazy_static;
```
```toml
# in Cargo.toml
[dependencies.lazy_static]
version = "0.2.4"
features = ["spin_no_std"]
```
We need the `spin_no_std` feature, since we don't link the standard library. We also need the `#[macro_use]` attribute on the `extern crate` line to import the `lazy_static!` macro.
Now we can create our static IDT using `lazy_static`:
```rust
lazy_static! {
static ref IDT: Idt = {
let mut idt = Idt::new();
idt.breakpoint.set_handler_fn(breakpoint_handler);
idt
};
}
pub fn init() {
IDT.load();
}
```
Note how this solution requires no `unsafe` blocks or `unwrap` calls.
> ##### Aside: How does the `lazy_static!` macro work?
>
> The macro generates a `static` of type `Once<Idt>`. The [`Once`][spin::Once] type is provided by the `spin` crate and allows deferred one-time initialization. It is implemented using an [`AtomicUsize`] for synchronization and an [`UnsafeCell`] for storing the (possibly uninitialized) value. So this solution also uses `unsafe` behind the scenes, but it is abstracted away in a safe interface.
[spin::Once]: https://docs.rs/spin/0.4.5/spin/struct.Once.html
[`AtomicUsize`]: https://doc.rust-lang.org/nightly/core/sync/atomic/struct.AtomicUsize.html
[`UnsafeCell`]: https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html
### Testing it
Now we should be able to handle breakpoint exceptions! Let's try it in our `rust_main`:
```rust
// in src/lib.rs
pub extern "C" fn rust_main(...) {
...
memory::init(boot_info);
// initialize our IDT
interrupts::init();
// invoke a breakpoint exception
x86_64::instructions::interrupts::int3();
println!("It did not crash!");
loop {}
}
```
When we run it in QEMU now (using `make run`), we see the following:
![QEMU printing `EXCEPTION: BREAKPOINT` and the exception stack frame](qemu-breakpoint-exception.png)
It works! The CPU successfully invokes our breakpoint handler, which prints the message, and then returns back to the `rust_main` function, where the `It did not crash!` message is printed.
> **Aside**: If it doesn't work and a boot loop occurs, this might be caused by a kernel stack overflow. Try increasing the stack size to at least 16kB (4096 * 4 bytes) in the `boot.asm` file.
We see that the exception stack frame tells us the instruction and stack pointers at the time when the exception occurred. This information is very useful when debugging unexpected exceptions. For example, we can look at the corresponding assembly line using `objdump`:
```
> objdump -d build/kernel-x86_64.bin | grep -B5 "1140a6:"
00000000001140a0 <x86_64::instructions::interrupts::int3::h015bf61815bb8afe>:
1140a0: 55 push %rbp
1140a1: 48 89 e5 mov %rsp,%rbp
1140a4: 50 push %rax
1140a5: cc int3
1140a6: 48 83 c4 08 add $0x8,%rsp
```
The `-d` flags disassembles the `code` section and `-C` flag makes function names more readable by [demangling] them. The `-B` flag of `grep` specifies the number of preceding lines that should be shown (5 in our case).
[demangling]: https://en.wikipedia.org/wiki/Name_mangling
We clearly see the `int3` exception that caused the breakpoint exception at address `1140a5`. Wait… the stored instruction pointer was `1140a6`, which is a normal `add` operation. What's happening here?
### Faults, Aborts, and Traps
The answer is that the stored instruction pointer only points to the causing instruction for _fault_ type exceptions, but not for _trap_ or _abort_ type exceptions. The difference between these types is the following:
- **Faults** are exceptions that can be corrected so that the program can continue as if nothing happened. An example is the [page fault], which can often be resolved by loading the accessed page from the disk into memory.
- **Aborts** are fatal exceptions that can't be recovered. Examples are [machine check exception] or the [double fault].
- **Traps** are only reported to the kernel, but don't hinder the continuation of the program. Examples are the breakpoint exception and the [overflow exception].
[page fault]: https://wiki.osdev.org/Exceptions#Page_Fault
[machine check exception]: https://wiki.osdev.org/Exceptions#Machine_Check
[double fault]: https://wiki.osdev.org/Exceptions#Double_Fault
[overflow exception]: https://wiki.osdev.org/Exceptions#Overflow
The reason for the diffent instruction pointer values is that the stored value is also the return address. So for faults, the instruction that caused the exception is restarted and might cause the same exception again if it's not resolved. This would not make much sense for traps, since invoking the breakpoint exception again would just cause another breakpoint exception[^fn-breakpoint-restart-use-cases]. Thus the instruction pointer points to the _next_ instruction for these exceptions.
In some cases, the distinction between faults and traps is vague. For example, the [debug exception] behaves like a fault in some cases, but like a trap in others. So to find out the meaning of the saved instruction pointer, it is a good idea to read the official documentation for the exception, which can be found in the [AMD64 manual] in Section 8.2. For example, for the breakpoint exception it says:
[debug exception]: https://wiki.osdev.org/Exceptions#Debug
[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
> `#BP` is a trap-type exception. The saved instruction pointer points to the byte after the `INT3` instruction.
The documentation of the [`Idt`] struct and the [OSDev Wiki][osdev wiki exceptions] also contain this information.
[`Idt`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.Idt.html
[osdev wiki exceptions]: https://wiki.osdev.org/Exceptions
## Too much Magic?
The `x86-interrupt` calling convention and the [`Idt`] type made the exception handling process relatively straightforward and painless. If this was too much magic for you and you like to learn all the gory details of exception handling, we got you covered: Our [“Handling Exceptions with Naked Functions”] series shows how to handle exceptions without the `x86-interrupt` calling convention and also creates its own `Idt` type. Historically, these posts were the main exception handling posts before the `x86-interrupt` calling convention and the `x86_64` crate existed.
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
## What's next?
We've successfully caught our first exception and returned from it! The next step is to add handlers for other common exceptions such as page faults. We also need to make sure that we never cause a [triple fault], since it causes a complete system reset. The next post explains how we can avoid this by correctly catching [double faults].
[triple fault]: https://wiki.osdev.org/Triple_Fault
[double faults]: https://wiki.osdev.org/Double_Fault#Double_Fault
## Footnotes
[^fn-breakpoint-restart-use-cases]: There are valid use cases for restarting an instruction that caused a breakpoint. The most common use case is a debugger: When setting a breakpoint on some code line, the debugger overwrites the corresponding instruction with an `int3` instruction, so that the CPU traps when that line is executed. When the user continues execution, the debugger swaps in the original instruction and continues the program from the replaced instruction.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -1,920 +0,0 @@
+++
title = "Double Faults"
weight = 10
path = "double-faults"
aliases = ["double-faults.html"]
date = 2017-01-02
template = "edition-1/page.html"
+++
In this post we explore double faults in detail. We also set up an _Interrupt Stack Table_ to catch double faults on a separate kernel stack. This way, we can completely prevent triple faults, even on kernel stack overflow.
<!-- more -->
As always, the complete source code is available on [GitHub]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a [gitter chat] and a comment section at the end of this page.
[GitHub]: https://github.com/phil-opp/blog_os/tree/first_edition_post_10
[issues]: https://github.com/phil-opp/blog_os/issues
[gitter chat]: https://gitter.im/phil-opp/blog_os
## What is a Double Fault?
In simplified terms, a double fault is a special exception that occurs when the CPU fails to invoke an exception handler. For example, it occurs when a page fault is triggered but there is no page fault handler registered in the [Interrupt Descriptor Table][IDT] (IDT). So it's kind of similar to catch-all blocks in programming languages with exceptions, e.g. `catch(...)` in C++ or `catch(Exception e)` in Java or C#.
[IDT]: @/edition-1/posts/09-handling-exceptions/index.md#the-interrupt-descriptor-table
A double fault behaves like a normal exception. It has the vector number `8` and we can define a normal handler function for it in the IDT. It is really important to provide a double fault handler, because if a double fault is unhandled a fatal _triple fault_ occurs. Triple faults can't be caught and most hardware reacts with a system reset.
### Triggering a Double Fault
Let's provoke a double fault by triggering an exception for that we didn't define a handler function:
```rust
// in src/lib.rs
#[no_mangle]
pub extern "C" fn rust_main(multiboot_information_address: usize) {
...
// initialize our IDT
interrupts::init();
// trigger a page fault
unsafe {
*(0xdeadbeaf as *mut u64) = 42;
};
println!("It did not crash!");
loop {}
}
```
We try to write to address `0xdeadbeaf`, but the corresponding page is not present in the page tables. Thus, a page fault occurs. We haven't registered a page fault handler in our [IDT], so a double fault occurs.
When we start our kernel now, we see that it enters an endless boot loop:
![boot loop](boot-loop.gif)
The reason for the boot loop is the following:
1. The CPU tries to write to `0xdeadbeaf`, which causes a page fault.
2. The CPU looks at the corresponding entry in the IDT and sees that the present bit isn't set. Thus, it can't call the page fault handler and a double fault occurs.
3. The CPU looks at the IDT entry of the double fault handler, but this entry is also non-present. Thus, a _triple_ fault occurs.
4. A triple fault is fatal. QEMU reacts to it like most real hardware and issues a system reset.
So in order to prevent this triple fault, we need to either provide a handler function for page faults or a double fault handler. Let's start with the latter, since we want to avoid triple faults in all cases.
### A Double Fault Handler
A double fault is a normal exception with an error code, so we can use our `handler_with_error_code` macro to create a wrapper function:
```rust
// in src/interrupts.rs
lazy_static! {
static ref IDT: idt::Idt = {
let mut idt = idt::Idt::new();
idt.breakpoint.set_handler_fn(breakpoint_handler);
idt.double_fault.set_handler_fn(double_fault_handler);
idt
};
}
// our new double fault handler
extern "x86-interrupt" fn double_fault_handler(
stack_frame: &mut ExceptionStackFrame, _error_code: u64)
{
println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
loop {}
}
```
Our handler prints a short error message and dumps the exception stack frame. The error code of the double fault handler is always zero, so there's no reason to print it.
When we start our kernel now, we should see that the double fault handler is invoked:
![QEMU printing `EXCEPTION: DOUBLE FAULT` and the exception stack frame](qemu-catch-double-fault.png)
It worked! Here is what happens this time:
1. The CPU executes tries to write to `0xdeadbeaf`, which causes a page fault.
2. Like before, the CPU looks at the corresponding entry in the IDT and sees that the present bit isn't set. Thus, a double fault occurs.
3. The CPU jumps to the now present double fault handler.
The triple fault (and the boot-loop) no longer occurs, since the CPU can now call the double fault handler.
That was quite straightforward! So why do we need a whole post for this topic? Well, we're now able to catch _most_ double faults, but there are some cases where our current approach doesn't suffice.
## Causes of Double Faults
Before we look at the special cases, we need to know the exact causes of double faults. Above, we used a pretty vague definition:
> A double fault is a special exception that occurs when the CPU fails to invoke an exception handler.
What does _“fails to invoke”_ mean exactly? The handler is not present? The handler is [swapped out]? And what happens if a handler causes exceptions itself?
[swapped out]: http://pages.cs.wisc.edu/~remzi/OSTEP/vm-beyondphys.pdf
For example, what happens if… :
1. a divide-by-zero exception occurs, but the corresponding handler function is swapped out?
2. a page fault occurs, but the page fault handler is swapped out?
3. a divide-by-zero handler causes a breakpoint exception, but the breakpoint handler is swapped out?
4. our kernel overflows its stack and the [guard page] is hit?
[guard page]: @/edition-1/posts/07-remap-the-kernel/index.md#creating-a-guard-page
Fortunately, the AMD64 manual ([PDF][AMD64 manual]) has an exact definition (in Section 8.2.9). According to it, a “double fault exception _can_ occur when a second exception occurs during the handling of a prior (first) exception handler”. The _“can”_ is important: Only very specific combinations of exceptions lead to a double fault. These combinations are:
First Exception | Second Exception
----------------|-----------------
[Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
[Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
[Divide-by-zero]: https://wiki.osdev.org/Exceptions#Division_Error
[Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS
[Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present
[Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault
[General Protection Fault]: https://wiki.osdev.org/Exceptions#General_Protection_Fault
[Page Fault]: https://wiki.osdev.org/Exceptions#Page_Fault
[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
So for example a divide-by-zero fault followed by a page fault is fine (the page fault handler is invoked), but a divide-by-zero fault followed by a general-protection fault leads to a double fault.
With the help of this table, we can answer the first three of the above questions:
1. If a divide-by-zero exception occurs and the corresponding handler function is swapped out, a _page fault_ occurs and the _page fault handler_ is invoked.
2. If a page fault occurs and the page fault handler is swapped out, a _double fault_ occurs and the _double fault handler_ is invoked.
3. If a divide-by-zero handler causes a breakpoint exception, the CPU tries to invoke the breakpoint handler. If the breakpoint handler is swapped out, a _page fault_ occurs and the _page fault handler_ is invoked.
In fact, even the case of a non-present handler follows this scheme: A non-present handler causes a _segment-not-present_ exception. We didn't define a segment-not-present handler, so another segment-not-present exception occurs. According to the table, this leads to a double fault.
### Kernel Stack Overflow
Let's look at the fourth question:
> What happens if our kernel overflows its stack and the [guard page] is hit?
When our kernel overflows its stack and hits the guard page, a _page fault_ occurs. The CPU looks up the page fault handler in the IDT and tries to push the [exception stack frame] onto the stack. However, our current stack pointer still points to the non-present guard page. Thus, a second page fault occurs, which causes a double fault (according to the above table).
[exception stack frame]: @/edition-1/posts/09-handling-exceptions/index.md#the-exception-stack-frame
So the CPU tries to call our _double fault handler_ now. However, on a double fault the CPU tries to push the exception stack frame, too. Our stack pointer still points to the guard page, so a _third_ page fault occurs, which causes a _triple fault_ and a system reboot. So our current double fault handler can't avoid a triple fault in this case.
Let's try it ourselves! We can easily provoke a kernel stack overflow by calling a function that recurses endlessly:
```rust
// in src/lib.rs
#[no_mangle]
pub extern "C" fn rust_main(multiboot_information_address: usize) {
...
// initialize our IDT
interrupts::init();
fn stack_overflow() {
stack_overflow(); // for each recursion, the return address is pushed
}
// trigger a stack overflow
stack_overflow();
println!("It did not crash!");
loop {}
}
```
When we try this code in QEMU, we see that the system enters a boot-loop again.
So how can we avoid this problem? We can't omit the pushing of the exception stack frame, since the CPU itself does it. So we need to ensure somehow that the stack is always valid when a double fault exception occurs. Fortunately, the x86_64 architecture has a solution to this problem.
## Switching Stacks
The x86_64 architecture is able to switch to a predefined, known-good stack when an exception occurs. This switch happens at hardware level, so it can be performed before the CPU pushes the exception stack frame.
This switching mechanism is implemented as an _Interrupt Stack Table_ (IST). The IST is a table of 7 pointers to known-good stacks. In Rust-like pseudo code:
```rust
struct InterruptStackTable {
stack_pointers: [Option<StackPointer>; 7],
}
```
For each exception handler, we can choose a stack from the IST through the `options` field in the corresponding [IDT entry]. For example, we could use the first stack in the IST for our double fault handler. Then the CPU would automatically switch to this stack whenever a double fault occurs. This switch would happen before anything is pushed, so it would prevent the triple fault.
[IDT entry]: @/edition-1/posts/09-handling-exceptions/index.md#the-interrupt-descriptor-table
### Allocating a new Stack
In order to fill an Interrupt Stack Table later, we need a way to allocate new stacks. Therefore we extend our `memory` module with a new `stack_allocator` submodule:
```rust
// in src/memory/mod.rs
mod stack_allocator;
```
First, we create a new `StackAllocator` struct and a constructor function:
```rust
// in src/memory/stack_allocator.rs
use memory::paging::PageIter;
pub struct StackAllocator {
range: PageIter,
}
impl StackAllocator {
pub fn new(page_range: PageIter) -> StackAllocator {
StackAllocator { range: page_range }
}
}
```
We create a simple `StackAllocator` that allocates stacks from a given range of pages (`PageIter` is an Iterator over a range of pages; we introduced it [in the kernel heap post].).
[in the kernel heap post]: @/edition-1/posts/08-kernel-heap/index.md#mapping-the-heap
We add a `alloc_stack` method that allocates a new stack:
```rust
// in src/memory/stack_allocator.rs
use memory::paging::{self, Page, ActivePageTable};
use memory::{PAGE_SIZE, FrameAllocator};
impl StackAllocator {
pub fn alloc_stack<FA: FrameAllocator>(&mut self,
active_table: &mut ActivePageTable,
frame_allocator: &mut FA,
size_in_pages: usize)
-> Option<Stack> {
if size_in_pages == 0 {
return None; /* a zero sized stack makes no sense */
}
// clone the range, since we only want to change it on success
let mut range = self.range.clone();
// try to allocate the stack pages and a guard page
let guard_page = range.next();
let stack_start = range.next();
let stack_end = if size_in_pages == 1 {
stack_start
} else {
// choose the (size_in_pages-2)th element, since index
// starts at 0 and we already allocated the start page
range.nth(size_in_pages - 2)
};
match (guard_page, stack_start, stack_end) {
(Some(_), Some(start), Some(end)) => {
// success! write back updated range
self.range = range;
// map stack pages to physical frames
for page in Page::range_inclusive(start, end) {
active_table.map(page, paging::WRITABLE, frame_allocator);
}
// create a new stack
let top_of_stack = end.start_address() + PAGE_SIZE;
Some(Stack::new(top_of_stack, start.start_address()))
}
_ => None, /* not enough pages */
}
}
}
```
The method takes mutable references to the [ActivePageTable] and a [FrameAllocator], since it needs to map the new virtual stack pages to physical frames. We define that the stack size is a multiple of the page size.
[ActivePageTable]: @/edition-1/posts/06-page-tables/index.md#page-table-ownership
[FrameAllocator]: @/edition-1/posts/05-allocating-frames/index.md#a-frame-allocator
Instead of operating directly on `self.range`, we [clone] it and only write it back on success. This way, subsequent stack allocations can still succeed if there are pages left (e.g., a call with `size_in_pages = 3` can still succeed after a failed call with `size_in_pages = 100`).
In order to be able to clone `PageIter`, we add a `#[derive(Clone)]` to its definition in `src/memory/paging/mod.rs`. We also need to make the `start_address` method of the `Page` type public (in the same file).
[clone]: https://doc.rust-lang.org/nightly/core/clone/trait.Clone.html#tymethod.clone
The actual allocation is straightforward: First, we choose the next page as [guard page]. Then we choose the next `size_in_pages` pages as stack pages using [Iterator::nth]. If all three variables are `Some`, the allocation succeeded and we map the stack pages to physical frames using [ActivePageTable::map]. The guard page remains unmapped.
[Iterator::nth]: https://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.nth
[ActivePageTable::map]: @/edition-1/posts/06-page-tables/index.md#more-mapping-functions
Finally, we create and return a new `Stack`, which we define as follows:
```rust
// in src/memory/stack_allocator.rs
#[derive(Debug)]
pub struct Stack {
top: usize,
bottom: usize,
}
impl Stack {
fn new(top: usize, bottom: usize) -> Stack {
assert!(top > bottom);
Stack {
top: top,
bottom: bottom,
}
}
pub fn top(&self) -> usize {
self.top
}
pub fn bottom(&self) -> usize {
self.bottom
}
}
```
The `Stack` struct describes a stack though its top and bottom addresses.
#### The Memory Controller
Now we're able to allocate a new double fault stack. However, we add one more level of abstraction to make things easier. For that we add a new `MemoryController` type to our `memory` module:
```rust
// in src/memory/mod.rs
pub use self::stack_allocator::Stack;
pub struct MemoryController {
active_table: paging::ActivePageTable,
frame_allocator: AreaFrameAllocator,
stack_allocator: stack_allocator::StackAllocator,
}
impl MemoryController {
pub fn alloc_stack(&mut self, size_in_pages: usize) -> Option<Stack> {
let &mut MemoryController { ref mut active_table,
ref mut frame_allocator,
ref mut stack_allocator } = self;
stack_allocator.alloc_stack(active_table, frame_allocator,
size_in_pages)
}
}
```
The `MemoryController` struct holds the three types that are required for `alloc_stack` and provides a simpler interface (only one argument). The `alloc_stack` wrapper just takes the tree types as `&mut` through [destructuring] and forwards them to the `stack_allocator`. The [ref mut]-s are needed to take the inner fields by mutable reference. Note that we're re-exporting the `Stack` type since it is returned by `alloc_stack`.
[destructuring]: https://doc.rust-lang.org/1.10.0/book/patterns.html#destructuring
[ref mut]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch18-03-pattern-syntax.html#creating-references-in-patterns-with-ref-and-ref-mut
The last step is to create a `StackAllocator` and return a `MemoryController` from `memory::init`:
```rust
// in src/memory/mod.rs
pub fn init(boot_info: &BootInformation) -> MemoryController {
...
let stack_allocator = {
let stack_alloc_start = heap_end_page + 1;
let stack_alloc_end = stack_alloc_start + 100;
let stack_alloc_range = Page::range_inclusive(stack_alloc_start,
stack_alloc_end);
stack_allocator::StackAllocator::new(stack_alloc_range)
};
MemoryController {
active_table: active_table,
frame_allocator: frame_allocator,
stack_allocator: stack_allocator,
}
}
```
We create a new `StackAllocator` with a range of 100 pages starting right after the last heap page.
In order to do arithmetic on pages (e.g. calculate the hundredth page after `stack_alloc_start`), we implement `Add<usize>` for `Page`:
```rust
// in src/memory/paging/mod.rs
use core::ops::Add;
impl Add<usize> for Page {
type Output = Page;
fn add(self, rhs: usize) -> Page {
Page { number: self.number + rhs }
}
}
```
#### Allocating a Double Fault Stack
Now we can allocate a new double fault stack by passing the memory controller to our `interrupts::init` function:
```rust
// in src/lib.rs
#[no_mangle]
pub extern "C" fn rust_main(multiboot_information_address: usize) {
...
// set up guard page and map the heap pages
let mut memory_controller = memory::init(boot_info); // new return type
// initialize our IDT
interrupts::init(&mut memory_controller); // new argument
...
}
// in src/interrupts.rs
use memory::MemoryController;
pub fn init(memory_controller: &mut MemoryController) {
let double_fault_stack = memory_controller.alloc_stack(1)
.expect("could not allocate double fault stack");
IDT.load();
}
```
We allocate a 4096 bytes stack (one page) for our double fault handler. Now we just need some way to tell the CPU that it should use this stack for handling double faults.
### The IST and TSS
The Interrupt Stack Table (IST) is part of an old legacy structure called _[Task State Segment]_ \(TSS). The TSS used to hold various information (e.g. processor register state) about a task in 32-bit mode and was for example used for [hardware context switching]. However, hardware context switching is no longer supported in 64-bit mode and the format of the TSS changed completely.
[Task State Segment]: https://en.wikipedia.org/wiki/Task_state_segment
[hardware context switching]: https://wiki.osdev.org/Context_Switching#Hardware_Context_Switching
On x86_64, the TSS no longer holds any task specific information at all. Instead, it holds two stack tables (the IST is one of them). The only common field between the 32-bit and 64-bit TSS is the pointer to the [I/O port permissions bitmap].
[I/O port permissions bitmap]: https://en.wikipedia.org/wiki/Task_state_segment#I.2FO_port_permissions
The 64-bit TSS has the following format:
Field | Type
------ | ----------------
<span style="opacity: 0.5">(reserved)</span> | `u32`
Privilege Stack Table | `[u64; 3]`
<span style="opacity: 0.5">(reserved)</span> | `u64`
Interrupt Stack Table | `[u64; 7]`
<span style="opacity: 0.5">(reserved)</span> | `u64`
<span style="opacity: 0.5">(reserved)</span> | `u16`
I/O Map Base Address | `u16`
The _Privilege Stack Table_ is used by the CPU when the privilege level changes. For example, if an exception occurs while the CPU is in user mode (privilege level 3), the CPU normally switches to kernel mode (privilege level 0) before invoking the exception handler. In that case, the CPU would switch to the 0th stack in the Privilege Stack Table (since 0 is the target privilege level). We don't have any user mode programs yet, so we ignore this table for now.
#### Creating a TSS
Let's create a new TSS that contains our double fault stack in its interrupt stack table. For that we need a TSS struct. Fortunately, the `x86_64` crate already contains a [`TaskStateSegment` struct] that we can use:
[`TaskStateSegment` struct]: https://docs.rs/x86_64/0.1.1/x86_64/structures/tss/struct.TaskStateSegment.html
```rust
// in src/interrupts.rs
use x86_64::structures::tss::TaskStateSegment;
```
Let's create a new TSS in our `interrupts::init` function:
```rust
// in src/interrupts.rs
use x86_64::VirtualAddress;
const DOUBLE_FAULT_IST_INDEX: usize = 0;
pub fn init(memory_controller: &mut MemoryController) {
let double_fault_stack = memory_controller.alloc_stack(1)
.expect("could not allocate double fault stack");
let mut tss = TaskStateSegment::new();
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX] = VirtualAddress(
double_fault_stack.top());
IDT.load();
}
```
We define that the 0th IST entry is the double fault stack (any other IST index would work too). We create a new TSS through the `TaskStateSegment::new` function and load the top address (stacks grow downwards) of the double fault stack into the 0th entry.
#### Loading the TSS
Now that we created a new TSS, we need a way to tell the CPU that it should use it. Unfortunately, this is a bit cumbersome, since the TSS is a Task State _Segment_ (for historical reasons). So instead of loading the table directly, we need to add a new segment descriptor to the [Global Descriptor Table] \(GDT). Then we can load our TSS invoking the [`ltr` instruction] with the respective GDT index.
[Global Descriptor Table]: https://web.archive.org/web/20190217233448/https://www.flingos.co.uk/docs/reference/Global-Descriptor-Table/
[`ltr` instruction]: https://www.felixcloutier.com/x86/ltr
### The Global Descriptor Table (again)
The Global Descriptor Table (GDT) is a relict that was used for [memory segmentation] before paging became the de facto standard. It is still needed in 64-bit mode for various things such as kernel/user mode configuration or TSS loading.
[memory segmentation]: https://en.wikipedia.org/wiki/X86_memory_segmentation
We already created a GDT [when switching to long mode]. Back then, we used assembly to create valid code and data segment descriptors, which were required to enter 64-bit mode. We could just edit that assembly file and add an additional TSS descriptor. However, we now have the expressiveness of Rust, so let's do it in Rust instead.
[when switching to long mode]: @/edition-1/posts/02-entering-longmode/index.md#the-global-descriptor-table
We start by creating a new `interrupts::gdt` submodule. For that we need to rename the `src/interrupts.rs` file to `src/interrupts/mod.rs`. Then we can create a new submodule:
```rust
// in src/interrupts/mod.rs
mod gdt;
```
```rust
// src/interrupts/gdt.rs
pub struct Gdt {
table: [u64; 8],
next_free: usize,
}
impl Gdt {
pub fn new() -> Gdt {
Gdt {
table: [0; 8],
next_free: 1,
}
}
}
```
We create a simple `Gdt` struct with two fields. The `table` field contains the actual GDT modeled as a `[u64; 8]`. Theoretically, a GDT can have up to 8192 entries, but this doesn't make much sense in 64-bit mode (since there is no real segmentation support). Eight entries should be more than enough for our system.
The `next_free` field stores the index of the next free entry. We initialize it with `1` since the 0th entry needs always needs to be 0 in a valid GDT.
#### User and System Segments
There are two types of GDT entries in long mode: user and system segment descriptors. Descriptors for code and data segment segments are user segment descriptors. They contain no addresses since segments always span the complete address space on x86_64 (real segmentation is no longer supported). Thus, user segment descriptors only contain a few flags (e.g. present or user mode) and fit into a single `u64` entry.
System descriptors such as TSS descriptors are different. They often contain a base address and a limit (e.g. TSS start and length) and thus need more than 64 bits. Therefore, system segments are 128 bits. They are stored as two consecutive entries in the GDT.
Consequently, we model a `Descriptor` as an `enum`:
```rust
// in src/interrupts/gdt.rs
pub enum Descriptor {
UserSegment(u64),
SystemSegment(u64, u64),
}
```
The flag bits are common between all descriptor types, so we create a general `DescriptorFlags` type (using the [bitflags] macro):
[bitflags]: https://docs.rs/bitflags/0.9.1/bitflags/macro.bitflags.html
```rust
// in src/interrupts/gdt.rs
bitflags! {
struct DescriptorFlags: u64 {
const CONFORMING = 1 << 42;
const EXECUTABLE = 1 << 43;
const USER_SEGMENT = 1 << 44;
const PRESENT = 1 << 47;
const LONG_MODE = 1 << 53;
}
}
```
We only add flags that are relevant in 64-bit mode. For example, we omit the read/write bit, since it is completely ignored by the CPU in 64-bit mode.
#### Code Segments
We add a function to create kernel mode code segments:
```rust
// in src/interrupts/gdt.rs
impl Descriptor {
pub fn kernel_code_segment() -> Descriptor {
let flags = USER_SEGMENT | PRESENT | EXECUTABLE | LONG_MODE;
Descriptor::UserSegment(flags.bits())
}
}
```
We set the `USER_SEGMENT` bit to indicate a 64 bit user segment descriptor (otherwise the CPU expects a 128 bit system segment descriptor). The `PRESENT`, `EXECUTABLE`, and `LONG_MODE` bits are also needed for a 64-bit mode code segment.
The data segment registers `ds`, `ss`, and `es` are completely ignored in 64-bit mode, so we don't need any data segment descriptors in our GDT.
#### TSS Segments
A TSS descriptor is a system segment descriptor with the following format:
Bit(s) | Name | Meaning
--------------------- | ------ | ----------------------------------
0-15 | **limit 0-15** | the first 2 byte of the TSS's limit
16-39 | **base 0-23** | the first 3 byte of the TSS's base address
40-43 | **type** | must be `0b1001` for an available 64-bit TSS
44 | zero | must be 0
45-46 | privilege | the [ring level]: 0 for kernel, 3 for user
47 | **present** | must be 1 for valid selectors
48-51 | limit 16-19 | bits 16 to 19 of the segment's limit
52 | available | freely available to the OS
53-54 | ignored |
55 | granularity | if it's set, the limit is the number of pages, else it's a byte number
56-63 | **base 24-31** | the fourth byte of the base address
64-95 | **base 32-63** | the last four bytes of the base address
96-127 | ignored/must be zero | bits 104-108 must be zero, the rest is ignored
[ring level]: https://wiki.osdev.org/Security#Rings
We only need the bold fields for our TSS descriptor. For example, we don't need the `limit 16-19` field since a TSS has a fixed size that is smaller than `2^16`.
Let's add a function to our descriptor that creates a TSS descriptor for a given TSS:
```rust
// in src/interrupts/gdt.rs
use x86_64::structures::tss::TaskStateSegment;
impl Descriptor {
pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor {
use core::mem::size_of;
use bit_field::BitField;
let ptr = tss as *const _ as u64;
let mut low = PRESENT.bits();
// base
low.set_bits(16..40, ptr.get_bits(0..24));
low.set_bits(56..64, ptr.get_bits(24..32));
// limit (the `-1` in needed since the bound is inclusive)
low.set_bits(0..16, (size_of::<TaskStateSegment>() - 1) as u64);
// type (0b1001 = available 64-bit tss)
low.set_bits(40..44, 0b1001);
let mut high = 0;
high.set_bits(0..32, ptr.get_bits(32..64));
Descriptor::SystemSegment(low, high)
}
}
```
The `set_bits` and `get_bits` methods are provided by the [`BitField` trait] of the `bit_fields` crate. They allow us to easily get or set specific bits in an integer without using bit masks or shift operations. For example, we can do `x.set_bits(8..12, 42)` instead of `x = (x & 0xfffff0ff) | (42 << 8)`.
[`BitField` trait]: https://docs.rs/bit_field/0.6.0/bit_field/trait.BitField.html#method.get_bit
To link the `bit_fields` crate, we modify our `Cargo.toml` and our `src/lib.rs`:
```toml
[dependencies]
bit_field = "0.7.0"
```
```rust
extern crate bit_field;
```
We require the `'static` lifetime for the `TaskStateSegment` reference, since the hardware might access it on every interrupt as long as the OS runs.
#### Adding Descriptors to the GDT
In order to add descriptors to the GDT, we add a `add_entry` method:
```rust
// in src/interrupts/gdt.rs
use x86_64::structures::gdt::SegmentSelector;
use x86_64::PrivilegeLevel;
impl Gdt {
pub fn add_entry(&mut self, entry: Descriptor) -> SegmentSelector {
let index = match entry {
Descriptor::UserSegment(value) => self.push(value),
Descriptor::SystemSegment(value_low, value_high) => {
let index = self.push(value_low);
self.push(value_high);
index
}
};
SegmentSelector::new(index as u16, PrivilegeLevel::Ring0)
}
}
```
For an user segment we just push the `u64` and remember the index. For a system segment, we push the low and high `u64` and use the index of the low value. We then use this index to return a new [SegmentSelector].
[SegmentSelector]: https://docs.rs/x86/0.8.0/x86/shared/segmentation/struct.SegmentSelector.html#method.new
The `push` method looks like this:
```rust
// in src/interrupts/gdt.rs
impl Gdt {
fn push(&mut self, value: u64) -> usize {
if self.next_free < self.table.len() {
let index = self.next_free;
self.table[index] = value;
self.next_free += 1;
index
} else {
panic!("GDT full");
}
}
}
```
The method just writes to the `next_free` entry and returns the corresponding index. If there is no free entry left, we panic since this likely indicates a programming error (we should never need to create more than two or three GDT entries for our kernel).
#### Loading the GDT
To load the GDT, we add a new `load` method:
```rust
// in src/interrupts/gdt.rs
impl Gdt {
pub fn load(&'static self) {
use x86_64::instructions::tables::{DescriptorTablePointer, lgdt};
use core::mem::size_of;
let ptr = DescriptorTablePointer {
base: self.table.as_ptr() as u64,
limit: (self.table.len() * size_of::<u64>() - 1) as u16,
};
unsafe { lgdt(&ptr) };
}
}
```
We use the [`DescriptorTablePointer` struct] and the [`lgdt` function] provided by the `x86_64` crate to load our GDT. Again, we require a `'static` reference since the GDT possibly needs to live for the rest of the run time.
[`DescriptorTablePointer` struct]: https://docs.rs/x86_64/0.1.1/x86_64/instructions/tables/struct.DescriptorTablePointer.html
[`lgdt` function]: https://docs.rs/x86_64/0.1.1/x86_64/instructions/tables/fn.lgdt.html
### Putting it together
We now have a double fault stack and are able to create and load a TSS (which contains an IST). So let's put everything together to catch kernel stack overflows.
We already created a new TSS in our `interrupts::init` function. Now we can load this TSS by creating a new GDT:
```rust
// in src/interrupts/mod.rs
pub fn init(memory_controller: &mut MemoryController) {
let double_fault_stack = memory_controller.alloc_stack(1)
.expect("could not allocate double fault stack");
let mut tss = TaskStateSegment::new();
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX] = VirtualAddress(
double_fault_stack.top());
let mut gdt = gdt::Gdt::new();
let code_selector = gdt.add_entry(gdt::Descriptor::kernel_code_segment());
let tss_selector = gdt.add_entry(gdt::Descriptor::tss_segment(&tss));
gdt.load();
IDT.load();
}
```
However, when we try to compile it, the following errors occur:
```
error: `tss` does not live long enough
--> src/interrupts/mod.rs:118:68
|
118 | let tss_selector = gdt.add_entry(gdt::Descriptor::tss_segment(&tss));
| does not live long enough ^^^
...
122 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error: `gdt` does not live long enough
--> src/interrupts/mod.rs:119:5
|
119 | gdt.load();
| ^^^ does not live long enough
...
122 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
```
The problem is that we require that the TSS and GDT are valid for the rest of the run time (i.e. for the `'static` lifetime). But our created `tss` and `gdt` live on the stack and are thus destroyed at the end of the `init` function. So how do we fix this problem?
We could allocate our TSS and GDT on the heap using `Box` and use [into_raw] and a bit of `unsafe` to convert it to `&'static` references ([RFC 1233] was closed unfortunately).
Alternatively, we could store them in a `static` somehow. The [`lazy_static` macro] doesn't work here, since we need access to the `MemoryController` for initialization. However, we can use its fundamental building block, the [`spin::Once` type].
[into_raw]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.into_raw
[RFC 1233]: https://github.com/rust-lang/rfcs/pull/1233
[`lazy_static` macro]: https://docs.rs/lazy_static/0.2.2/lazy_static/
[`spin::Once` type]: https://docs.rs/spin/0.4.5/spin/struct.Once.html
#### spin::Once
Let's try to solve our problem using [`spin::Once`][`spin::Once` type]:
```rust
// in src/interrupts/mod.rs
use spin::Once;
static TSS: Once<TaskStateSegment> = Once::new();
static GDT: Once<gdt::Gdt> = Once::new();
```
The `Once` type allows us to initialize a `static` at runtime. It is safe because the only way to access the static value is through the provided methods ([call_once][Once::call_once], [try][Once::try], and [wait][Once::wait]). Thus, no value can be read before initialization and the value can only be initialized once.
[Once::call_once]: https://docs.rs/spin/0.4.5/spin/struct.Once.html#method.call_once
[Once::try]: https://docs.rs/spin/0.4.5/spin/struct.Once.html#method.try
[Once::wait]: https://docs.rs/spin/0.4.5/spin/struct.Once.html#method.wait
(The `Once` was added in spin 0.4, so you're probably need to update your spin dependency.)
So let's rewrite our `interrupts::init` function to use the static `TSS` and `GDT`:
```rust
pub fn init(memory_controller: &mut MemoryController) {
let double_fault_stack = memory_controller.alloc_stack(1)
.expect("could not allocate double fault stack");
let tss = TSS.call_once(|| {
let mut tss = TaskStateSegment::new();
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX] = VirtualAddress(
double_fault_stack.top());
tss
});
let gdt = GDT.call_once(|| {
let mut gdt = gdt::Gdt::new();
let code_selector = gdt.add_entry(gdt::Descriptor::
kernel_code_segment());
let tss_selector = gdt.add_entry(gdt::Descriptor::tss_segment(&tss));
gdt
});
gdt.load();
IDT.load();
}
```
Now it should compile again!
#### The final Steps
We're almost done. We successfully loaded our new GDT, which contains a TSS descriptor. Now there are just a few steps left:
1. We changed our GDT, so we should reload the `cs`, the code segment register. This required since the old segment selector could point a different GDT descriptor now (e.g. a TSS descriptor).
2. We loaded a GDT that contains a TSS selector, but we still need to tell the CPU that it should use that TSS.
3. As soon as our TSS is loaded, the CPU has access to a valid interrupt stack table (IST). Then we can tell the CPU that it should use our new double fault stack by modifying our double fault IDT entry.
For the first two steps, we need access to the `code_selector` and `tss_selector` variables outside of the closure. We can achieve this by moving the `let` declarations out of the closure:
```rust
// in src/interrupts/mod.rs
pub fn init(memory_controller: &mut MemoryController) {
use x86_64::structures::gdt::SegmentSelector;
use x86_64::instructions::segmentation::set_cs;
use x86_64::instructions::tables::load_tss;
...
let mut code_selector = SegmentSelector(0);
let mut tss_selector = SegmentSelector(0);
let gdt = GDT.call_once(|| {
let mut gdt = gdt::Gdt::new();
code_selector = gdt.add_entry(gdt::Descriptor::kernel_code_segment());
tss_selector = gdt.add_entry(gdt::Descriptor::tss_segment(&tss));
gdt
});
gdt.load();
unsafe {
// reload code segment register
set_cs(code_selector);
// load TSS
load_tss(tss_selector);
}
IDT.load();
}
```
We first set the descriptors to `empty` and then update them from inside the closure (which implicitly borrows them as `&mut`). Now we're able to reload the code segment register using [`set_cs`] and to load the TSS using [`load_tss`].
[`set_cs`]: https://docs.rs/x86_64/0.1.2/x86_64/instructions/segmentation/fn.set_cs.html
[`load_tss`]: https://docs.rs/x86_64/0.1.2/x86_64/instructions/tables/fn.load_tss.html
Now that we loaded a valid TSS and interrupt stack table, we can set the stack index for our double fault handler in the IDT:
```rust
// in src/interrupt/mod.rs
lazy_static! {
static ref IDT: idt::Idt = {
let mut idt = idt::Idt::new();
...
unsafe {
idt.double_fault.set_handler_fn(double_fault_handler)
.set_stack_index(DOUBLE_FAULT_IST_INDEX as u16);
}
...
};
}
```
The `set_stack_index` method is unsafe because the the caller must ensure that the used index is valid and not already used for another exception.
That's it! Now the CPU should switch to the double fault stack whenever a double fault occurs. Thus, we are able to catch _all_ double faults, including kernel stack overflows:
![QEMU printing `EXCEPTION: DOUBLE FAULT` and a dump of the exception stack frame](qemu-double-fault-on-stack-overflow.png)
From now on we should never see a triple fault again!
## What's next?
Now that we mastered exceptions, it's time to explore another kind of interrupts: interrupts from external devices such as timers, keyboards, or network controllers. These hardware interrupts are very similar to exceptions, e.g. they are also dispatched through the IDT.
However, unlike exceptions, they don't arise directly on the CPU. Instead, an _interrupt controller_ aggregates these interrupts and forwards them to CPU depending on their priority. In the next posts we will explore the two interrupt controller variants on x86: the [Intel 8259] \(“PIC”) and the [APIC]. This will allow us to react to keyboard and mouse input.
[Intel 8259]: https://en.wikipedia.org/wiki/Intel_8259
[APIC]: https://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,6 +0,0 @@
+++
title = "Posts"
sort_by = "weight"
insert_anchor_links = "left"
render = false
+++

View File

@@ -1,5 +0,0 @@
+++
title = "Second Edition"
template = "redirect-to-frontpage.html"
aliases = ["second-edition/index.html"]
+++

View File

@@ -1,7 +0,0 @@
+++
title = "Extra Content"
insert_anchor_links = "left"
render = false
sort_by = "weight"
page_template = "edition-2/extra.html"
+++

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

View File

@@ -1,115 +0,0 @@
+++
title = "Building on Android"
weight = 3
aliases = ["second-edition/extra/building-on-android/index.html"]
+++
I finally managed to get `blog_os` building on my Android phone using [termux](https://termux.com/). This post explains the necessary steps to set it up.
<!-- more -->
<div class = "warning">
This post is outdated and the instructions provided here might not work anymore.
</div>
<img src="building-on-android.png" alt="Screenshot of the compilation output from android" style="height: 50rem;" >
### Install Termux and Nightly Rust
First, install [termux](https://termux.com/) from the [Google Play Store](https://play.google.com/store/apps/details?id=com.termux) or from F-Droid. After installing, open it and perform the following steps:
- Install fish shell, set as default shell, and launch it:
```
pkg install fish
chsh -s fish
fish
```
This step is of course optional. However, if you continue with bash you will need to adjust some of the following commands to bash syntax.
- Install some basic tools:
```
pkg install wget tar
```
- Add the [community repository by its-pointless](https://wiki.termux.com/wiki/Package_Management):
```
wget https://its-pointless.github.io/setup-pointless-repo.sh
bash setup-pointless-repo.sh
```
- Install cargo and a nightly version of rustc:
```
pkg install rustc cargo rustc-nightly
```
- Prepend the nightly rustc path to your `PATH` in order to use nightly (fish syntax):
```
set -U fish_user_paths $PREFIX/opt/rust-nightly/bin/ $fish_user_paths
```
Now `rustc --version` should work and output a nightly version number.
### Install Git and Clone blog_os
We need something to compile, so let's download the `blog_os` repository:
- Install git:
```
pkg install git
```
- Clone the `blog_os` repository:
```
git clone https://github.com/phil-opp/blog_os.git
```
If you want to clone/push via SSH, you need to install the `openssh` package: `pkg install openssh`.
### Install Xbuild and Bootimage
Now we're ready to install `cargo xbuild` and `bootimage`
- Run `cargo install`:
```
cargo install cargo-xbuild bootimage
```
- Add the cargo bin directory to your `PATH` (fish syntax):
```
set -U fish_user_paths ~/.cargo/bin/ $fish_user_paths
```
Now `cargo xbuild` and `bootimage` should be available. It does not work yet because `cargo xbuild` needs access to the rust source code. By default it tries to use rustup for this, but we have no rustup support so we need a different way.
### Providing the Rust Source Code
The Rust source code corresponding to our installed nightly is available in the [`its-pointless` repository](https://github.com/its-pointless/its-pointless.github.io):
- Download a tar containing the source code:
```
wget https://github.com/its-pointless/its-pointless.github.io/raw/master/rust-src-nightly.tar.xz
```
- Extract it:
```
tar xf rust-src-nightly.tar.xz
```
- Set the `XARGO_RUST_SRC` environment variable to tell cargo-xbuild the source path (fish syntax):
```
set -Ux XARGO_RUST_SRC ~/rust-src-nightly/rust-src/lib/rustlib/src/rust/src
```
Now cargo-xbuild should no longer complain about a missing `rust-src` component. However it will throw an I/O error after building the sysroot. The problem is that the downloaded Rust source code has a different structure than the source provided by rustup. We can fix this by adding a symbolic link:
```
ln -s ~/../usr/opt/rust-nightly/bin ~/../usr/opt/rust-nightly/lib/rustlib/aarch64-linux-android/bin
```
Now `cargo xbuild --target x86_64-blog_os.json` and `bootimage build` should both work!
I couldn't get QEMU to run yet, so you won't be able to run your kernel. If you manage to get it working, please tell me :).

View File

@@ -1,129 +0,0 @@
+++
title = "A Freestanding Rust Binary"
weight = 1
path = "ar/freestanding-rust-binary"
date = 2018-02-10
[extra]
# Please update this when updating the translation
translation_based_on_commit = "087a464ed77361cff6c459fb42fc655cb9eacbea"
# GitHub usernames of the people that translated this post
translators = ["ZAAFHachemrachid"]
+++
تتمثل الخطوة الأولى في إنشاء نواة نظام التشغيل الخاصة بنا في إنشاء ملف Rust قابل للتنفيذ لا يربط المكتبة القياسية. هذا يجعل من الممكن تشغيل شيفرة Rust على [bare metal] دون نظام تشغيل أساسي.
[bare metal]: https://en.wikipedia.org/wiki/Bare_machine
<!-- more -->
تم تطوير هذه المدونة بشكل مفتوح على [GitHub]. إذا كان لديك أي مشاكل أو أسئلة، يرجى فتح مشكلة هناك. يمكنك أيضًا ترك تعليقات [في الأسفل]. يمكن العثور على الشيفرة المصدرية الكاملة لهذا المنشور في فرع [post-01][post branch].
[GitHub]: https://github.com/phil-opp/blog_os
[at the bottom]: #comments
<!-- fix for zola anchor checker (target is in template): <a id="comments"> -->
[post branch]: https://github.com/phil-opp/blog_os/tree/post-01
<!-- toc -->
## مقدمة
لكتابة نواة نظام تشغيل، نحتاج إلى شيفرة لا تعتمد على أي ميزات نظام تشغيل. هذا يعني أنه لا يمكننا استخدام سلاسل الرسائل(threads) أو الملفات(File System) أو Heap ram أو الشبكة أو الأرقام العشوائية أو الإخراج القياسي(I/O) أو أي ميزات أخرى تتطلب تجريدات نظام التشغيل أو أجهزة معينة. وهذا منطقي، لأننا نحاول كتابة نظام التشغيل الخاص بنا (OS) وبرامج التشغيل الخاصة بنا (drivers).
هذا يعني أنه لا يمكننا استخدام معظم [Rust standard library]، ولكن هناك الكثير من ميزات Rust التي _يمكننا استخدامها. على سبيل المثال، يمكننا استخدام [iterators] و [closures] و [pattern matching] و [option] و [اresult] و [string formatting] وبالطبع [ownership system]. هذه الميزات تجعل من الممكن كتابة نواة بطريقة معبرة جدًا وعالية المستوى دون القلق بشأن [undefined behavior] أو [memory safety].
[option]: https://doc.rust-lang.org/core/option/
[result]:https://doc.rust-lang.org/core/result/
[Rust standard library]: https://doc.rust-lang.org/std/
[iterators]: https://doc.rust-lang.org/book/ch13-02-iterators.html
[closures]: https://doc.rust-lang.org/book/ch13-01-closures.html
[pattern matching]: https://doc.rust-lang.org/book/ch06-00-enums.html
[string formatting]: https://doc.rust-lang.org/core/macro.write.html
[ownership system]: https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html
[undefined behavior]: https://www.nayuki.io/page/undefined-behavior-in-c-and-cplusplus-programs
[memory safety]: https://tonyarcieri.com/it-s-time-for-a-memory-safety-intervention
من أجل إنشاء نواة نظام تشغيل في Rust، نحتاج إلى إنشاء ملف قابل للتنفيذ يمكن تشغيله بدون نظام تشغيل أساسي. غالبًا ما يُطلق على هذا الملف القابل للتنفيذ اسم الملف القابل للتنفيذ ”القائم بذاته“ أو ”المعدني العاري“.
يصف هذا المنشور الخطوات اللازمة لإنشاء ثنائي Rust قائم بذاته ويشرح سبب الحاجة إلى هذه الخطوات. إذا كنت مهتمًا بمثال بسيط فقط، يمكنك **[الانتقال إلى الملخص] (#ملخص)**.
## تعطيل المكتبة القياسية
بشكل افتراضي، تربط جميع صناديق Rust [standard library]، والتي تعتمد على نظام التشغيل لميزات (مثل threads, files, or networking). كما أنها تعتمد أيضًا على مكتبة C القياسية 'libc'، والتي تتفاعل بشكل وثيق مع خدمات نظام التشغيل. نظرًا لأن خطتنا هي كتابة نظام تشغيل، لا يمكننا استخدام أي مكتبات تعتمد على نظام التشغيل. لذا يجب علينا تعطيل التضمين التلقائي للمكتبة القياسية من خلال سمة [no_std].
[standard library]: https://doc.rust-lang.org/std/
[`no_std` attribute]: https://doc.rust-lang.org/1.30.0/book/first-edition/using-rust-without-the-standard-library.html
```
cargo new blog_os --bin --edition 2018
```
لقد أطلقتُ على المشروع اسم ”Blog_os“، ولكن بالطبع يمكنك اختيار اسمك الخاص. تُحدّد علامة ”bin“ أننا نريد إنشاء نسخة binary قابلة للتنفيذ (على عكس المكتبة) وتحدّد علامة ”--- Edition 2018“ أننا نريد استخدام [2018 edition] من Rust لصندوقنا. عندما نُشغّل الأمر، تُنشئ لنا الشحنة بنية الدليل التالية:
[2018 edition]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/index.html
```
blog_os
├── Cargo.toml
└── src
└── main.rs
```
يحتوي ملف 'Cargo.toml' على تكوين الصندوق، على سبيل المثال اسم الصندوق، والمؤلف، ورقم [semantic version]، والتبعيات. يحتوي الملف 'src/main.rs' على الوحدة النمطية الجذرية للصندوق والدالة 'الرئيسية'. يمكنك تجميع قفصك من خلال 'cargo build' ثم تشغيل الملف الثنائي 'blog_os' المجمّع في المجلد الفرعي 'target/debug'.
[semantic version]: https://semver.org/
### السمة 'no_std'
يربط صندوقنا الآن المكتبة القياسية ضمنيًا بالمكتبة القياسية. دعونا نحاول تعطيل ذلك بإضافة سمة [no_std]:
```rust
// main.rs
#![no_std]
fn main() {
println!("Hello, world!");
}
```
عندما نحاول بناءه الآن (عن طريق تشغيل ”cargo build“)، يحدث الخطأ التالي:
```
error: cannot find macro `println!` in this scope
--> src/main.rs:4:5
|
4 | println!("Hello, world!");
| ^^^^^^^
```
والسبب في هذا الخطأ هو أن [`println` macro] هو جزء من المكتبة القياسية، والتي لم نعد نضمّنها. لذا لم يعد بإمكاننا طباعة الأشياء. هذا أمر منطقي، لأن 'println' يكتب إلى [standard output]، وهو واصف ملف خاص يوفره نظام التشغيل.
[`println` macro]: https://doc.rust-lang.org/std/macro.println.html
[standard output]: https://en.wikipedia.org/wiki/Standard_streams#Standard_output_.28stdout.29
لذا دعنا نحذف الطباعة ونحاول مرة أخرى بدالة رئيسية فارغة:
```rust
// main.rs
#![no_std]
fn main() {}
```
```
> cargo build
error: `#[panic_handler]` function required, but not found
error: language item required, but not found: `eh_personality`
```
يفتقد بناء المترجمات البرمجية الآن إلى `#[panic_handler]` دالة و _language item_.

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