次のパートの時が来ました。 これは、翻訳ラボ番号1の後半です。 この問題では、周辺機器ドライバー(タイマー、GPIO、UART)を記述し、XMODEMプロトコルと1つのユーティリティを実装します。 これらすべてを使用して、カーネルのコマンドシェルと、microSDカードを前後に動かさないようにするブートローダーを作成します。
フェーズ3:貝殻ではない

今回は、統合された周辺機器用のドライバーをいくつか作成します。 組み込みタイマー、GPIO、およびUARTに興味があります。 それらは組み込みのコマンドラインを書くのに十分であり、少し後にブートローダーを作成するのに便利になります(これにより、さらに作業がいくらか簡単になります)。
ドライバーとは何ですか?
ドライバーまたはデバイスドライバーという用語は、特定のハードウェアデバイスと直接対話し、それを管理するソフトウェアなどです。 ドライバーは、制御するデバイスに高レベルのインターフェイスを提供します。 オペレーティングシステムは、デバイスドライバーと対話して、それらの上にさらに高度な抽象化を構築します(もちろん、便宜上)。 たとえば、Linuxカーネルは、ドライバーと対話するオーディオ用のAPIであるALSA(Advanced Linux Sound Architecture)を提供し、ドライバーはサウンドカードと直接通信します。
サブフェーズA:はじめに
割り当ての残りの部分では、 os
カブの内部で作業します。これは、この部分だけでなく、コースの残りの部分でも使用されます。 最終的にオペレーティングシステムになるのは、このリポジトリです。
このコースを参照して、ラボなどすべてに次のディレクトリ構造をお勧めします。
cs140e ├── 0-blinky │ ├── Makefile │ ├── phase3 │ └── phase4 ├── 1-shell │ ├── ferris-wheel │ ├── getting-started │ ├── stack-vec │ ├── ttywrite │ ├── volatile │ └── xmodem └── os ├── Makefile ├── bootloader ├── kernel ├── pi ├── std └── volatile
快適で端正な。 0-blinky
および1-shell
は、以前のラボと現在のラボを指し、 os
は次のように取得できます。
git clone https://web.stanford.edu/class/cs140e/os.git os git checkout master
すべてが正しく配置されていることを確認し、 os/kernel
内でmake
を実行しos/kernel
。 すべてが正常であれば、コマンドは正常に実行されます。
プロジェクト構造
os
ディレクトリには、次の一連のサブディレクトリが含まれます。
-
pi
は、ドライバーとOS用の低レベルコードを含むライブラリです。 -
volatile
フェーズ2からの同じ名前のライブラリの2番目のバージョン -
std
-Rust標準ライブラリの最小限のスタブ -
bootloader
-フェーズ4で追加するブートローダー -
kernel
-OSのメインカーネル
すべてのドライバーコードはpi
ライブラリにあります。 pi
はvolatile
ライブラリと(オプションで) std
ます。 kernel
とbootloader
はpi
を使用してデバイスと通信します。 そして、これに加えて、それらはstd
に依存していstd
。 volatile
は何にも依存しません。 グラフィカルに、この関係は次のようになります。
ファームウェア
続行する前に、ラズベリーファームウェアを更新する必要があります。 これらはすべて、 os
ディレクトリからmake fetch
コマンドでダウンロードできます。 必要な素材をfiles/
ロードしfiles/
。 firmware/bootcode.bin
、 firmware/config.txt
およびfirmware/start.elf
をmicroSDカードのルートにコピーしfirmware/bootcode.bin
。 前の部分からact-led-blink.bin
コピーし、名前をkernel8.img
に変更kernel8.img
ます。 したがって、すべてが機能することを確認できます。 そこで、ラズベリー自体の緑色のLEDが点滅します。
更新されたvolatile
os
フォルダーにあるこのライブラリーは、フェーズ2で検討したコードとは少し異なります。変更により、デバイスドライバーの作成のコンテキストでこれを少し簡単に使用できるようになります。 主な違いは次のとおりです。
-
UniqueVolatile
がUnique<Volatile>
置き換えられました -
Reserved
タイプを追加しました。これは絶対に何もできず、スタブとして使用されます
もう1つ、より重要な違いがあります。 ライブラリのすべての型は、 *mut T
T
ではなくT
ラップします*mut T
これにより、ラッピングせずにあらゆる種類の生のアドレスを使用し、次のようにキャストできます0x1000 as *mut Volatile<T>
。 これに加えて、 Volatile
ラップされたフィールドを含む構造を指定できます。 このようなもの:
#[repr(C)] struct Registers { REG_A: Volatile<u32>, REG_B: Volatile<u8> } // . `Registers` `0x4000` . // `u32` `u8` // (. ). let x: *mut Registers = 0x4000 as *mut Registers; // `unsafe`. // Rust unsafe { // Rust (*x).REG_A.write(434); let val: u8 = (*x).REG_B.read(); }
#[repr(C)]
とは何ですか?
追記#[repr(C)]
は、RustがSmallと同じ方法でメモリ内に構造を形成するように強制します。 これがなければ、Rustにはフィールドの順序とフィールド間のインデントを最適化する権利があります。 生のポインタを使用する場合、ほとんどの場合、メモリ内の特定の構造を意味します。 したがって、#[repr(C)]
使用すると、Rustが想定どおりに構造をメモリに配置することをアサートできます。
コア
os/kernel
ディレクトリには、OSのカーネルコードの準備が含まれています。 このディレクトリ内のmake
呼び出しは、カーネルを収集します。 アセンブリの結果はbuild/
サブディレクトリにあります。 このビジネスを開始するには、 build/kernel.bin
をkernel8.img
という名前でmicroSDカードのルートにコピーbuild/kernel.bin
必要があります。 現在、カーネルは何もしていません。 このフェーズの終わりまでに、カーネルにはチャット可能なテキストベースの対話型シェルが含まれます。
kernel
ラックはpi
ラックに依存します。 extern crate pi;
を見ることができますextern crate pi;
kernel/src/kmain.rs
に記述しCargo.toml
。 つまり pi
宣言されたすべての型と構造を非選択的に使用できます。
ドキュメント
ドライバーを作成するとき、 BCM2837ペリフェラルマニュアルは非常に役立ちます。
サブフェーズB:システムタイマー

このサブフェーズでは、組み込みタイマーのドライバーを作成します。 主な作業は、ファイルos/pi/src/timer.rs
およびos/kernel/src/kmain.rs
ます。 タイマーは、 BCM2837周辺機器マニュアルの 172ページ(セクション12)に記載されています。
最初に、すでにos/pi/src/timer.rs
あるコードを見てos/pi/src/timer.rs
。 少なくともこれらの部分:
const TIMER_REG_BASE: usize = IO_BASE + 0x3000; #[repr(C)] struct Registers { CS: Volatile<u32>, CLO: ReadVolatile<u32>, CHI: ReadVolatile<u32>, COMPARE: [Volatile<u32>; 4] } pub struct Timer { registers: &'static mut Registers } impl Timer { pub fn new() -> Timer { Timer { registers: unsafe { &mut *(TIMER_REG_BASE as *mut Registers) }, } } }
最初に注意する必要があるunsafe
でunsafe
コードの1行があります。 *mut Registers
のアドレスTIMER_REG_BASE
はこの行にキャストされ、その後すぐに&'static mut Registers
に変わります。 実際、 TIMER_REG_BASE
の構造への静的リンクが必要であることをTIMER_REG_BASE
ています。
TIMER_REG_BASE
には正確に何がありますか? マニュアルの 172ページで、 0x3000
がタイマーの周辺の先頭からのオフセットであることがわかります。 つまり TIMER_REG_BASE
は、このタイマーのレジスタが始まるアドレスです。 unsafe
でunsafe
1行の後に、 registers
フィールドを使用して、これらすべてに完全に安全にアクセスできます。 たとえば、 self.registers.CLO.read()
をself.registers.CLO.read()
てCLO
レジスタをself.registers.CLO.read()
か、または
self.registers.CS.write()
を使用するCS
。
なぜCLO
およびCHI
レジスタに書き込めないのですか? [制限付き読み取り]
BCM2837のドキュメントには、CLO
およびCHI
レジスタが読み取り専用であると記載されています。 コードはこのプロパティを提供します。 どうやって? 何がCLO
とCHI
書くことを妨げますか?
安全ではないものは何ですか?
要するに、unsafe
はRustコンパイラーのマーカーであり、ユーザーはメモリーのセキュリティを制御していると言います。 コンパイラーは、これらのコードのメモリー問題からユーザーを保護しません。 コードのunsafe
部分では、Rustを使用して、Neat Cで実行できるすべての操作を実行できます。 できるコラバンを奪うあるタイプを別のタイプにキャストし、生のポインターで遊んで、寿命を作成するだけで十分です。
ただし、unsafe
ブロック内のコードは非常に危険です。 安全でないセクションで行うことは、実際に安全であることを確認する必要があります。 これは一見思われるよりも複雑です。 特に、Rustのセキュリティ概念は他の言語よりも厳格であるためです。unsafe
でないものを使用しunsafe
してください。 もちろん、可能な範囲で。 オペレーティングシステムなどの場合、機器と直接通信する場合は、unsafe
を使用する必要があります。 ただし、この使用はできる限り制限します。
unsafe
モアレを読みたい場合は、Nomiconの第1章をご覧ください 。 さらに、この小さな本を通して、Rustのさまざまな強力な魔術師に役立つ多くのことを学ぶことができます。
ドライバーの実装
os/pi/src/timer.rs
Timer::read()
os/pi/src/timer.rs
からTimer::read()
実装しos/pi/src/timer.rs
。 次に、メソッドcurrent_time()
、 spin_sleep_us()
、 spin_sleep_ms()
が近くにあります。 これらの関数のコメントと名前は、期待される機能を示しています。 Timer::read()
を実装するには、適切なセクションのBCM2837ドキュメントを読む必要がありTimer::read()
。 少なくとも、64ビットタイマー値全体を取得するために読み取る必要があるレジスタを理解する必要があります。 cargo build
して、パイクレートを組み立てることができます。 ただし、 cargo check
を使用して記述された内容の正確性を単純にチェックする方が高速です。
ドライバーテスト
spin_sleep_ms()
関数が正しく実装されていることを確認することはspin_sleep_ms()
です。 これを行うには、 kernel/src/kmain.rs
適切なコードを記述しkernel/src/kmain.rs
。
ゼロラボのフェーズ4からLEDの点滅コードをコピーします。 ループ内でスピンするだけのスリープ関数の代わりに、 spin_sleep_ms()
関数を使用して、点滅間の休止を作成する必要があります。 カーネルを再コンパイルし、 kernel8.img
というメモリカードにロードします。 すべてを実行して、計画した頻度でLEDが点滅することを確認します。 別の遅延サイズを設定して、すべてが機能することを確認してください。 はい、microsdカードを常に前後に突くのは非常に面倒です。 このパートの終わりまでに、この問題を解決するブートローダーができます。
タイマーのドライバー実装が機能する場合、次のサブフェーズに進むことができます。
サブフェーズC:GPIO
このサブフェーズでは、特定のピン番号に関係なく、汎用GPIOドライバーを作成します。 主な作業は、ファイルos/pi/src/gpio.rs
およびos/kernel/src/kmain.rs
ます。 GPIOのドキュメントは、 BCM2837周辺機器マニュアルの 89ページ(セクション6)にあります 。
ステートマシン

すべてのハードウェアデバイスは、本質的にステートマシン ( eng )と見なすことができます。 それらはある状態で初期化され、他の状態に明示的またはそうではありません。 同時に、デバイスは現在の状態に応じてさまざまな機能を提供します。 つまり、特定の特定の状態では、他の状態への特定の遷移セットのみが動作可能です。
ほとんどのプログラミング言語では、有限状態マシンのセマンティクスを正確に追跡することは不可能です。 しかし、もちろんこれはRustには当てはまりません。 Rustを使用すると、このセマンティクスを非常に明確に追跡できます。 これを使用して、より安全なGPIOドライバーを実装します。 ドライバーは、すべてのGPIOピンが常に正しく使用されるようにします。 コンパイル段階。
* ある種の研究のように見える...
あなたは私を捕まえた。 これは本質的に、現時点での私の研究分野です。 - セルジオ
以下に、GPIOステートマシンプロパティのサブセット(1ピン)の状態図を示します。
私たちの目標は、これらすべてをRustで実装することです。 そもそも、sobsnaは次の図を教えてくれます。
- GPIOは
START
状態からSTART
ます -
START
状態から、次の状態に移動できます。
-
ALT
、他の状態への遷移はありません -
OUTPUT
-それ自体への2つの使用可能な遷移:SET
およびCLEAR
-
INPUT
-LEVEL
という名前の遷移が1つ
-
ラボ0ではどのようなトランジションを使用しましたか? [まばたき状態]
Lab 0からフェーズ4のコードを記述したとき、本質的にステートマシンのサブセットを暗黙的に実装しました。 この場合、状態間のどのような遷移が実現しましたか?
Rustタイプシステムを使用して、ピンがOUTPUT
状態にある場合にのみSET
およびCLEAR
、 INPUT
状態にある場合にLEVEL
のみが可能であることを保証します。 pi/src/gpio.rs
のGPIO
構造宣言を見てください:
pub struct Gpio<State> { pin: u8, registers: &'static mut Registers, _state: PhantomData<State> }
構造体には、 State
という一般的な引数が1つあります。 PhantomData
のみが使用し、他のPhantomData
は使用しません。 実際、そのようなPhantomDataのために、構造が何らかの形で一般化された引数を使用していることをRustに納得させるために存在します。 Gpio
状態のマーカーとしてState
を使用Gpio
ます。 同時に、このパラメーターの特定の値を作成できないようにする必要があります。
マクロstate!
そこにあると思われる型を生成しますが、作成することはできません。 この場合、 Gpio
の状態のリストが生成さGpio
ます。
states! { Uninitialized, Input, Output, Alt } // - : enum Input { }
変に見えます 。 可能な値なしで列挙を作成する必要があるのはなぜですか? 彼らは1つの快適なプロパティがあります。 作成できません。 ただし、マーカーとして使用できます。 Input
型の値は作成できないため、だれも渡すことができません。 それらはタイプのレベルでのみ存在し、他のどこにも存在しません。
次に、適切な遷移セットを使用して各状態のメソッドを実装できます。
impl Gpio<Output> { /// pub fn set(&mut self) { ... } /// pub fn clear(&mut self) { ... } } impl Gpio<Input> { /// pub fn level(&mut self) -> bool { ... } }
これは、 Gpio
が状態に応じて厳密に定義された方法でのみGpio
できるという保証のように見えます。 悪くないでしょ? しかし、これらの状態をどのように達成するのでしょうか? これを行うには、 Gpio::transition()
メソッドがあります。
impl<T> Gpio<T> { fn transition<S>(self) -> Gpio<S> { Gpio { pin: self.pin, registers: self.registers, _state: PhantomData } } }
この方法を使用すると、 Gpio
をある状態から別の状態に簡単かつ自由に転送できます。 状態T
Gpio
を取得し、状態S
Gpio
をGpio
S
すべてのS
およびT
で機能することに注意してくださいT
このメソッドは非常に慎重に使用する必要があります。 このすべてに間違いを犯した場合、ドライバーは誤って記述されていると見なされます。
transition()
を使用するには、 Gpio<S>
タイプS
を指定する必要があります。 Rustに十分な情報を提供して、Rustがすべて自分で取得できるようにします。 たとえば、 into_output
メソッドの実装:
pub fn into_output(self) -> Gpio<Output> { self.into_alt(Function::Output).transition() }
このメソッドでは、戻り値の型がGpio<Output>
ことが必要です。 Rust型システムはtransition()
呼び出しを調べると、 Gpio::transition()
メソッドをGpio<Output>
ます。このメソッドはGpio<Output>
を返します。 彼は、任意のS
存在するGpio<S>
を返すメソッドを見つけますS
したがって、 S
代わりにS
Output
を安全に置き換えることができます。 その結果、 Gpio<Alt>
( into_alt
関数から)をGpio<Output>
に変換します。
クライアントが任意の状態を渡すことができる場合はどうなりますか? [偽の状態]
ユーザーコードがGpio
構造の初期状態を自由に選択できるようにするとどうなるかを考えてください。 何がおかしいのでしょうか?
なぜこれがすべてRustでしかできないのですか?
into_
遷移は移動のセマンティクスを使用するという小さな事実に注意してください。 これは、Gpio
が別の状態になるとすぐに、以前の状態ではアクセスできなくなることを意味します。 型がClone
、Copy
およびその他の複製メソッドを実装するまで、逆遷移は使用できません。 他の言語ではそれができません。C++
でも。 すべての保証付きのコンパイル中のそのような魔術師はここだけです。 (プラスや他の何かの達人は、この声明に挑戦しようとすることができます)
ドライバーの実装
unimplemented!()
代わりに必要なコードをすべて書いてunimplemented!()
ファイルpi/src/gpio.rs
これらすべての方法のコメントと署名から、理解できます 控除を使用して 期待される機能。 ドキュメント(89ページ、 BCM2837マニュアルのセクション6)を参照することは不要です。 cargo check
の有用性を忘れないでcargo check
。
ヒント:中括弧{ ... }
を使用して任意の字句スコープを作成できることを忘れないでください。
ドライバーテスト
明らかに、ドライバーをテストするには、 kernel/src/kmain.rs
いくつかのコードを書く必要がありkernel/src/kmain.rs
。
今回は、レジスタ自体を直接読み書きする代わりに、LEDを点滅させるためにドライバーを使用します。 GPIOピン番号16をオン/オフすることにより、同時に、コード全体がよりクリーンでエレガントになります。 カーネルをコンパイルし、 kernel8.img
というカードにロードして、 kernel8.img
ラズベリーを実行します。 LEDは以前とまったく同じように点滅するはずです。
これで、より多くのLEDを接続できます。 5、6、13、19、26の番号が付けられたGPIOピンを使用します。ゼロラボのピンの番号付きの図を参照して、物理的な位置を確認します。 あなたが望むように多くのLEDでコアを点滅させてください!
どの点滅パターンを選択しましたか? [LEDパターン]
LEDをオン/オフすることにしたスキームは何ですか? 好みに合わせて多くのオプションから選択できます。 ただし、選択が厳しい場合は、サークルでオンとオフを切り替えることができます。
GPIOドライバーが完全に動作可能になったら、次のサブフェーズに進むことができます。
サブフェーズD:UART
このサブフェーズでは、ミニUARTデバイスドライバーを作成します。これは、ラズベリーの割合に組み込まれています。 ほとんどの作業は、ファイルos/pi/src/uart.rs
およびos/kernel/src/kmain.rs
ます。 Mini UARTは、 BCM2837マニュアルの 8ページと10ページ(セクション2.1と2.2)に記載されています 。
UART:ユニバーサル非同期RX / TX
UART ( ru )またはUniversal Synchronous Reception Transmitterは、腺を2本のワイヤで通信するためのデバイスおよびシリアルプロトコルです。 これらは、CP2102 USBモジュールのUARTデバイスをラズベリーのUARTデバイスに接続するために、ゼロラボのフェーズ1で使用されたものと同じ2つの配線(rx / tx)です。 UARTを介して任意のデータを送信できます。テキスト、バイナリファイル、シール付きの写真など、ファンタジーに十分なものです。 例として、次のサブフェーズで、ラズベリーのUARTから読み取り、CP2102のUARTに書き込む対話型シェルを作成します。 フェーズ4では、ほぼ同じ方法でバイナリ情報を送信します。
UARTプロトコルにはいくつかの構成パラメーターがあります。 すべてが機能するためには、受信機と送信機の両方を同一に構成する必要があります。 これらは次のパラメーターです。
- データサイズ - データを含む1フレームの長さ(8または9ビット)
- パリティビット -データの後にパリティビット(制御ビット)を送信するかどうか
- ストップビット -データが送信されたことを判断するために使用するビット数(1または2ビット)
- ボーレート -ビットレート/秒
Mini UARTはパリティビットをサポートせず、1つのストップビットのみをサポートします。 したがって、ボーレートとフレーム長を設定するだけです。 UART通信の基本 (翻訳が必要ですか?)というドキュメントで、UART自体についてもう少し読むことができます。
ドライバーの実装
この段階では、各ステップをペイントすることなくデバイスドライバーを作成するために必要なすべてのツールが用意されています。 おめでとうございます!
タスクは、必要なすべてをファイルpi/src/uart.rs
に実装することです。 Registers
構造のコンテンツを追加する必要があります。 この場合、各レジスタに最低限必要な機能のセットを備えた[ Volatile
タイプ]オプションを使用します。 これらの読み取り専用レジスタはReadVolatile
を使用する必要があります。 WriteVolatile
のみWriteVolatile
許可されている場合、 WriteVolatile
。 予約スペースにはReserved
ます。 new()
115200 ( 270) 8 . unimplemented!()
, . fmt::Write
, io::Read
io::Write
MiniUart
.
:LCR
,BAUD
CNTL
new
/
: GPIO .
, ( kernel/src/kmain.rs
), . :
loop { write_byte(read_byte()) }
screen /dev/<_> 115200
UART. screen
TTY . , . つまり . :
loop { write_byte(read_byte()) write_str("<-") }
, — .
E: The Shell
UART . os/kernel/src/console.rs
, os/kernel/src/shell.rs
os/kernel/src/kmain.rs
.
Console

, , - / . Unix stdin
stdout
. Console
. Console
kprint!
kprintln!
。 , print!
println!
。 . Console
, .
os/kernel/src/console.rs
. Console
. - MiniUart
. . . MiniUart
MiniUart
Console
.
— , . Rust. Rust . , ? , unsafe
. : Rust'y, , .. , "" . , . Rust . :
// ! fn make_mut<T>(value: &T) -> &mut T { unsafe { /* */ } }
. . , , unsafe
Rust. , . " ". つまり . .
, . , . , ( &
). , ( &T -> &mut
).
. , , :
fn lock<T>(value: &T) -> Locked<&mut T> { unsafe { lock(value); cast value to Locked<&mut T> } } impl Drop for Locked<&mut T> { fn drop(&mut self) { unlock(self.value) } }
Mutex . — , :
fn get_mut<T>(value: &T) -> Mut<&mut T> { unsafe { if ref_count(value) != 0 { panic!() } ref_count(value) += 1; cast value to Mut<&mut T> } } impl Drop for Mut<&mut T> { fn drop(&mut self) { ref_count(value) -= 1; } }
, RefCell::borrow_mut() . — , :
fn get_mut<T>(value: &T) -> Option<Mut<&mut T>> { unsafe { if ref_count(value) != 0 { None } else { ref_count(value) += 1; Some(cast value to Mut<&mut T>) } } } impl Drop for Mut<&mut T> { fn drop(&mut self) { ref_count(value) -= 1; } }
RefCell::try_borrow_mut() . " ": . Console
Mutex
. std::Mutex
— . . kernel/src/mutex.rs
. , , , Rust. Mutex
, , .
だから。 CONSOLE
kernel/src/console.rs
. kprint!
kprintln!
, . , Console
— Console
. CONSOLE
Console
.
RustSync
.
T
static
,T
Sync
. , Rust . , Rust , .Send
Sync
, Rust .
**&mut T
? [drop-container]
, ,Drop
. ,&mut T
?
write_fmt
? [write-fmt]
_print
write_fmt
MutexGuard
(Mutex<Console>::lock()
.write_fmt
?
Console
, unimplemented!()
kernel/src/console.rs
. kprint!
kprintln!
, kernel/src/kmain.rs
, , . , print!
println!
。 screen /dev/<-> 115200
.
...
println!
— Rust.printf
. Rust , , . . , ? .
:Console
: .
. kernel/src/shell.rs
. Command
. Command::parse()
Command
. parse
args
StackVec
, buf
. Command::path()
.
( Command
, StackVec
, Console
CONSOLE
, kprint!
, kprintln!
, ) shell
. prefix
, . "> "
. , . ad-infinitum . . echo
.
, :
-
echo $a $b $c
,$a $b $c
-
\r
\n
enter
, - backspace delete (ASCII 8 127)
- (ASCII 7),
-
unknown command: $command
$command
- ,
- 512
- 64
-
prefix
, -
error: too many arguments
shell
. kernel/src/kmain.rs
. SOS, , . , . — . .
:
b'a'
u8
'a'
\u{b}
ASCIIb
\r
\n
, backspace, , backspace
StackVec
std::str::from_utf8
std
!
,std
. . , ,xargo doc --open
os/std
.
? [shell-lookback]
. , .
4:
, , Raspberry Pi. os/bootloader/src/kmain.rs
, MicroSD- . , , . , .
— "", , XMODEM UART. , . ttywrite
. :
-
ttywrite -i -.bin /dev/<->
Raspberyy Pi 3 kernel8.img
0x80000
. , , kernel8.img
0x80000
ARM' (program counter) 0x80000
. , . , 0x80000
.
(linker, ). . : , . , . os/kernel/ext/layout.ld
( ). , 0x80000
. 0x80000
.
, , 0x80000
. . 0x80000
. つまり ! . . . . どうやって?
. os/bootloader/ext/layout.ld
, , 0x4000000
. , 0x80000
. kernel_address
config.txt
. bootloader/ext/config.txt
. , . つまり MicroSD-.
0x80000
0x4000000
"" .
63.5 ? [small-kernels]
, , , . — . , . ?
, . macOS -/System/Library/Kernels/kernel
./mach_kernel
. Linux -/boot/
vmlinuz
,vmlinux
bzImage
. ? 63.5 ?
bootloader/src/kmain.rs
. , , . const
. jump_to
, addr
. . pi
xmodem
UART, , . , .
, XMODEM, ( 750 ). . — . , — . os/kernel/build/kernel.bin
ttywrite
. — , screen
.
? [bootloader-timeout]
. ?
config.txt
, !
:
kmain()
15 .
std::slice::from_raw_parts_mut .
&mut [u8]
io::Write
.