自作OSでシャットダウン実装してみた

# はじめに

こんにちは．[だいみょーじん](https://x.com/Egh2Deywos)です．
この記事は，[第41回自作OSもくもく会](https://osdev-jp.connpass.com/event/330081/)および[第42回自作OSもくもく会](https://osdev-jp.connpass.com/event/336197/)で発表した内容をまとめ，[自作OSアドベントカレンダー2024](https://adventar.org/calendars/10539)の25日目の記事として公開したものです．
私が開発しているRust製のOS，[HeliOS](https://github.com/TaiseiIto/HeliOS)におけるシャットダウンについて解説します．

# 環境

HeliOSはx64アーキテクチャ上で動作するOSで，以降の説明においてもx64アーキテクチャを想定します．
また，電源管理規格については，ACPIを想定します．
動作確認は以下の4通りの環境で行いました．

* QEMU
* VirtualBox
* VMware
* GPD MicroPC

# あなたのOS，シャットダウンできますか？

シャットダウンは，OSが備えるべき重要な機能のひとつです．
しかし，OS自作におけるシャットダウンの実装は難易度が高く，シャットダウンを実装していないOSも多いです．
「３０日でできる！OS自作入門」（ISBN 9784839919849）や「ゼロからのOS自作入門」（ISBN 9784839975869）ではシャットダウンは実装されていませんし，「作って理解するOS」（ISBN 9784297108472）もBochsでのシャットダウンにのみ対応しています．
（参考：[「作って理解するOS」を読んで「QEMUで電断できねーじゃんか！」になった人を救いたい」](https://qiita.com/Egh2Deywos/items/9b3ea5b5bf0046499067)）
今回私はx64アーキテクチャ上で動作するRust製の自作OS，HeliOSに，電源管理規格ACPIに従ったシャットダウンを実装したので，その原理についてこの記事にまとめます．

# UEFIによるシャットダウン

そんな難しそうなシャットダウンですが，実は割と簡単な方法もあります．
それはUEFIのランタイムサービスに含まれるResetSystem関数を呼び出すという方法です．

[UEFI Specification](https://uefi.org/sites/default/files/resources/UEFI_Spec_2_9_2021_03_18.pdf) 8.5.1
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/72f64b31-cc02-e974-588a-4165565323cc.png)

しかし，この方法では一部の環境でシャットダウンできないことが確認されました．

|実行環境|ResetSystem関数によるシャットダウン|
|:-|:-|
|QEMU|成功|
|VirtualBox|失敗|
|VMware|成功|
|GPD MicroPC|成功|

確実にシャットダウンしたい場合は，やはり電源管理規格ACPIを直接いじるべきでしょう．

# ACPIによるシャットダウン

[ACPI Specification](https://uefi.org/sites/default/files/resources/ACPI_Spec_6_5_Aug29.pdf) 7.4.2に，以下の6つの電源状態が定義されています．

|電源状態|CPUの命令の実行|CPUのレジスタ|主記憶|
|:-|:-|:-|:-|
|S0|実行|保持|保持|
|S1|停止|保持|保持|
|S2|停止|破棄|保持|
|S3|停止|破棄|保持|
|S4|停止|破棄|補助記憶に退避|
|S2|停止|破棄|破棄|

S0が通常の実行状態で，数値が大きくなるほど深いスリープ状態となり，S5がシャットダウンされた状態です．
S2とS3にソフトウェアから見た違いはありませんが，ハードウェア面でS3の方がより消費電力が少なくなるそうです．
今回は，実行状態S0から，停止状態S5に遷移することを目指します．

## DSDTとAMLとASL

電源状態を遷移する手順は，ACPI System Description TableのひとつDSDT（Differentiated System Description Table）にAML（ACPI Machine Language）というバイナリ言語で記述されています．
HeliOSにおけるDSDTのソースコードは[こちら](https://github.com/TaiseiIto/HeliOS/blob/main/kernel/src/acpi/differentiated_system_description.rs)
AMLの文法はACPI Specificationの第20章で定義され，約350種類の非終端記号と，0x00から0xffまでの256種類の終端記号を持ちます．

また，バイナリ形式の言語であるAMLに対応するテキスト形式の言語としてASL（ACPI Source Language）があります．
ASLの文法はACPI Specificationの第19章で定義されます．
AMLとASLはiaslというコマンドを使って相互に翻訳可能です．
シャットダウンの実装に必要なのはAMLのみですが，AMLは機械に読ませる言語であり人間には非常に読みずらいです．
AMLの解析器を開発する上で，手元にAMLをASLに翻訳したものも持っておいて，両者を照らし合わせながら開発を進めることをおすすめします．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/c69f29fe-061f-179e-d6b1-702d6fd425ae.png)

## AML構文解析のイメージをつかむ

さて，シャットダウンするために電源状態を遷移する手順が書かれたAMLを解析したいわけですが，AMLを解析するプログラムを書く前にまずは自分の手でAMLを解析して感覚をつかみましょう．
今回は練習として，以下のAMLコードを解析してみます．

```
44 42 47 5f
```

これを表計算ソフトか何かに入力します．

<table>
 <tr>
  <td>
0x44
  </td>
  <td>
0x42
  </td>
  <td>
0x47
  </td>
  <td>
0x5f
  </td>
 </tr>
</table>

そして上に行を追加し，セルを結合して`NameString`と記入します．

<table>
 <tr>
  <td colspan="4">
NameString
  </td>
 </tr>
 <tr>
  <td>
0x44
  </td>
  <td>
0x42
  </td>
  <td>
0x47
  </td>
  <td>
0x5f
  </td>
 </tr>
</table>

`<NameString>`は非終端記号の一つで，今回解析する4バイト全体が`<NameString>`という非終端記号から導出されることを示します．
今は例としてAMLの中の`<NameString>`の部分を抜き出して解析しているので`<NameString>`から解析を開始していますが，AMLは本来`<AMLCode>`を開始記号とするので，AML全体を解析する場合は`<AMLCode>`から解析を開始しなければならないことに注意してください．

### 1バイト目の解析

1バイト目は`0x44`つまり大文字の`D`です．
ACPI specificationの第20章に記述されている生成規則を見て，`<NameString>`から`D`に至る経路を探し出します．
すると以下のような経路が見つかります．

```
<NameString> ::= <RoorChar><NamePath> | <PrefixPath><NamePath>
<PrefixPath> ::= ε | <ParentPrefixChar><PrefixPath>
<NamePath> ::= <NameSeg> | <DualNamePath> | <MultiNamePath> | <NullName>
<NameSeg> ::= <LeadNameChar><NameChar><NameChar><NameChar>
<LeandNameChar> ::= 'A' | 'B' | 'C' | 'D' | ... | 'Z' | '_'
```

この記事では生成規則をバッカス・ナウア記法で記述することとします．
まず，`<NameString>`が`<PrefixPath><NamePath>`に展開されます．
次に，`<PrefixPath>`が`ε`つまり空文字列に展開されます．
次に，`<NamePath>`が`<NameSeg>`に展開されます．
次に，`<NameSeg>`が`<LeadNameChar><NameChar><NameChar><NameChar>`
最後に，`<LeadNameChar>`が`D`つまり`0x44`に展開されます．
この経路を，表に記入します．

<table>
 <tr>
  <td colspan="4">
NameString
  </td>
 </tr>
 <tr>
  <td>
NamePath
  </td>
  <td>
  </td>
  <td>
  </td>
  <td>
  </td>
 </tr>
 <tr>
  <td>
NameSeg
  </td>
  <td>
  </td>
  <td>
  </td>
  <td>
  </td>
 </tr>
 <tr>
  <td>
LeadNameChar
  </td>
  <td>
  </td>
  <td>
  </td>
  <td>
  </td>
 </tr>
 <tr>
  <td>
0x44
  </td>
  <td>
0x42
  </td>
  <td>
0x47
  </td>
  <td>
0x5f
  </td>
 </tr>
</table>

### 2バイト目の解析

2バイト目は`0x42`つまり大文字の`B`です．
`<NameSeg>`は`<LeadNameChar>`の後に`<NameChar>`が続いているので，`<NameChar>`から`B`に至る経路を探し出します．
すると以下のような経路が見つかります．

```
<NameChar> ::= <DigitChar> | <LeadNameChar>
<LeadNameChar> ::= 'A' | 'B' | ... | 'Z' | '_'
```

まず，`<NameChar>`が`<LeadNameChar>`に展開されます．
次に，`<LeadNameChar>`が`B`つまり`0x42`に展開されます．
この経路を，表に記入します．

<table>
 <tr>
  <td colspan="4">
NameString
  </td>
 </tr>
 <tr>
  <td colspan="2">
NamePath
  </td>
  <td>
  </td>
  <td>
  </td>
 </tr>
 <tr>
  <td colspan="2">
NameSeg
  </td>
  <td>
  </td>
  <td>
  </td>
 </tr>
 <tr>
  <td>
LeadNameChar
  </td>
  <td>
NameChar
  </td>
  <td>
  </td>
  <td>
  </td>
 </tr>
 <tr>
  <td>
  </td>
  <td>
LeadNameChar
  </td>
  <td>
  </td>
  <td>
  </td>
 </tr>
 <tr>
  <td>
0x44
  </td>
  <td>
0x42
  </td>
  <td>
0x47
  </td>
  <td>
0x5f
  </td>
 </tr>
</table>

### 3バイト目の解析

3バイト目は`0x47`つまり大文字の`G`です．
`<NameSeg>`から展開された2つ目の`<NameChar>`から`G`に至る経路を探し出します．
すると以下のような経路が見つかります．

```
<NameChar> ::= <DigitChar> | <LeadNameChar>
<LeadNameChar> ::= 'A' | ... | `G` | ... | 'Z' | '_'
```

まず，`<NameChar>`が`<LeadNameChar>`に展開されます．
次に，`<LeadNameChar>`が`G`つまり`0x47`に展開されます．
この経路を，表に記入します．

<table>
 <tr>
  <td colspan="4">
NameString
  </td>
 </tr>
 <tr>
  <td colspan="2">
NamePath
  </td>
  <td>
  </td>
  <td>
  </td>
 </tr>
 <tr>
  <td colspan="2">
NameSeg
  </td>
  <td>
  </td>
  <td>
  </td>
 </tr>
 <tr>
  <td>
LeadNameChar
  </td>
  <td>
NameChar
  </td>
  <td>
NameChar
  </td>
  <td>
  </td>
 </tr>
 <tr>
  <td>
  </td>
  <td>
LeadNameChar
  </td>
  <td>
LeadNameChar
  </td>
  <td>
  </td>
 </tr>
 <tr>
  <td>
0x44
  </td>
  <td>
0x42
  </td>
  <td>
0x47
  </td>
  <td>
0x5f
  </td>
 </tr>
</table>

### 4バイト目の解析

4バイト目は`0x5f`つまりアンダースコアです．
`<NameSeg>`から展開された3つ目の`<NameChar>`からアンダースコアに至る経路を探し出します．
すると以下のような経路が見つかります．

```
<NameChar> ::= <DigitChar> | <LeadNameChar>
<LeadNameChar> ::= 'A' | ... | 'Z' | '_'
```

まず，`<NameChar>`が`<LeadNameChar>`に展開されます．
次に，`<LeadNameChar>`が`_`つまり`0x5f`に展開されます．
この経路を，表に記入します．

<table>
 <tr>
  <td colspan="4">
NameString
  </td>
 </tr>
 <tr>
  <td colspan="4">
NamePath
  </td>
 </tr>
 <tr>
  <td colspan="4">
NameSeg
  </td>
 </tr>
 <tr>
  <td>
LeadNameChar
  </td>
  <td>
NameChar
  </td>
  <td>
NameChar
  </td>
  <td>
NameChar
  </td>
 </tr>
 <tr>
  <td>
  </td>
  <td>
LeadNameChar
  </td>
  <td>
LeadNameChar
  </td>
  <td>
LeadNameChar
  </td>
 </tr>
 <tr>
  <td>
0x44
  </td>
  <td>
0x42
  </td>
  <td>
0x47
  </td>
  <td>
0x5f
  </td>
 </tr>
</table>

今回解析した4バイトのAMLは，`DBG_`という名前を意味しています．
そして，その4バイトを解析した結果できた上の表がまさに構文木となっているのです．

## RustでAMLを構文解析

手動構文解析でAMLの感覚をつかんだところで，次は構文解析を行うプログラムをRustで書いていきましょう．

### 非終端記号の定義

AMLを解析するにあたって，まずは非終端記号をデータ型として表現します．
いくつか例を挙げます．

#### 非終端記号`<ExtOpPrefix>`

生成規則

```
<ExtOpPrefix> ::= 0x5b
```

に対して，

```Rust
pub struct ExtOpPrefix;
```

と定義します．

#### 非終端記号`<DefOpRegion>`

生成規則

```
<DefOpRegion> ::= <OpRegionOp> <NameString> <RegionSpace> <RegionOffset> <RegionLen>
```

に対して，

```Rust
pub struct DefOpRegion(
    OpRegionOp,
    NameString,
    RegionSpace,
    RegionOffset,
    RegionLen,
);
```

と定義します．

#### 非終端記号`<DataObject>`

生成規則

```
<DataObject> ::= <ComputationalData> | <DefPackage> | <DefVarPackage>
```

に対して，

```Rust
pub enum DataObject {
    ComputationalData(ComputationalData),
    DefPackage(DefPackage),
    DefVarPackage(DefVarPackage),
}
```

と定義します．
HeliOSにおけるこれらのデータ型の定義は[こちら](https://github.com/TaiseiIto/HeliOS/blob/main/kernel/src/acpi/machine_language/syntax.rs)

### Readerトレイト

データ型として表現した非終端記号に，以下の`Reader`トレイトを実装します．

```Rust
pub trait Reader {
    fn matches(aml: &[u8]) -> bool where Self: Sized;
    fn read(aml: &[u8]) -> (Self, &[u8]) where Self: Sized;
}
```

`matches`関数は，引数として受け取ったAMLのバイト列の先頭が，非終端記号`Self`として解釈可能かどうかを返します．
`read`関数は，引数として受け取ったAMLのバイト列を構文解析し，非終端記号`Self`を根とする構文木と，構文解析されなかった残りのAMLのバイト列を返します．

read関数のイメージ
![ReaderTrait.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/3507be0c-f414-381e-2f4c-1d549ce0c4ca.png)

`Reader`トレイトの実装例を見てみましょう．

#### 非終端記号`<ExtOpPrefix>`の`Reader`トレイト実装

```Rust
impl Reader for ExtOpPrefix {
    fn matches(aml: &[u8]) -> bool {
        aml.first().is_some_and(|byte| *byte == 0x5b)
    }
    
    fn read(aml: &[u8]) -> (Self, &[u8]) {
        (Self, &aml[1..])
    }
}
```

`<ExtOpPrefix>`の生成規則は

```
<ExtOpPrefix> ::= 0x5b
```

です．
`matches`関数は，AMLのバイト列の先頭バイトが存在しその値が`0x5b`であるときに，そのAMLの先頭を`<ExtOpPrefix>`として解釈可能であると判定します．
`read`関数は，`Self`と，AMLのバイト列の先頭の`0x5b`を取り除いた残りのバイト列を返します．

#### 非終端記号`<DefOpRegion>`の`Reader`トレイト実装

```Rust
impl Reader for DefOpRegion {
    fn matches(aml: &[u8]) -> bool {
        OpRegionOp::matches(aml)
    }

    fn read(aml: &[u8]) -> (Self, &[u8]) {
        let (op_region_op, aml) = OpRegionOp::read(aml);
        let (name_string, aml) = NameString::read(aml);
        let (reagion_space, aml) = RegionSpace::read(aml);
        let (region_offset, aml) = RegionOffset::read(aml);
        let (region_len, aml) = RegionLen::read(aml);
        (Self(
            op_region_op,
            name_string,
            region_space,
            region_offset,
            region_len,
        ), aml)
    }
}
```

`<DefOpRegion`の生成規則は

```
<DefOpRegion> ::= <OpRegionOp><NameString><RegionSpace><RegionOffset><RegionLen>
```

です．
`matches`関数は，AMLのバイト列の先頭が`<OpRegionOp>`として解釈できる場合，それは`<DefOpRegion>`としても解釈できると判定します．
`read`関数は，まずAMLのバイト列`aml`を`<OpRegionOp>`として解析し，解析結果`op_region_op`と残りのバイト列`aml`を取得します．
次にその`aml`を`<NameString>`として解析し，解析結果`name_string`と残りのバイト列`aml`を取得します．
次にその`aml`を`<RegionSpace>`として解析し，解析結果`region_space`と残りのバイト列`aml`を取得します．
次にその`aml`を`<RegionOffset>`として解析し，解析結果`region_offset`と残りのバイト列`aml`を取得します．
次にその`aml`を`<RegionLen>`として解析し，解析結果`region_len`と残りのバイト列`aml`を取得します．
最後に，`op_region_op`，`name_string`，`region_space`，`region_offset`，`region_len`を`Self`としてまとめ，残りのバイト列`aml`と共に返します．

#### 非終端記号`<DataObject>`の`Reader`トレイト実装

```Rust
impl Reader for DataObject {
    fn matches(aml: &[u8]) -> bool {
        ComputationalData::maches(aml)
        | DefPackage::matches(aml)
        | DefVarPackage::matches(aml)
    }
    
    fn read(aml: &[u8]) -> (Self, &[u8]) {
        if ComputationalData::matches(aml) {
            let (computational_data, aml) = ComputationalData::read(aml);
            (Self::ComputationalData(computational_data), aml)
        } else if DefPackage::matches(aml) {
            let (def_package, aml) = DefPackage::read(aml);
            (Self::DefPackage(def_package), aml)
        } else if DefVarPackage::matches(aml) {
            let (def_var_package, aml) = DefVarPackage::read(aml);
            (Self::DefVarPackage(def_var_package), aml)
        } else {
            panic!("Invalud DataObject!")
        }
    }
}
```

`<DataObject>`の生成規則は

```
<DataObject> ::= <ComputationalData> | <DefPackage> | <DefVarPackage>
```

です．
`matches`関数は，AMLのバイト列の先頭が`<ComputationalData>`または`<DefPackage>`または`<DefVarPackage>`として解釈できる場合，それは`<DataObject>`としても解釈できると判定します．
`read`関数は，AMLのバイト列の先頭が`<ComputationalData>`として解釈できる場合，`<ComputationalData>`として解析したものを`Self::ComputationalData`の中に入れ，残りのバイト列と一緒に返します．
AMLのバイト列の先頭が`<DefPackage>`として解釈できる場合，`<DefPackage>`として解析したものを`Self::DefPackage`の中に入れ，残りのバイト列と一緒に返します．
AMLのバイト列の先頭が`<DefVarPackage>`として解釈できる場合，`<DefVarPackage>`として解析したものを`Self::DefVarPackage`の中に入れ，残りのバイト列と一緒に返します．

### トレイト実装の自動化

上に示したように約350種類のAMLの非終端記号全てに`Reader`トレイトを実装していきます．
ひたすら実装していきます．
めげずに実装していきます．
なんかもうパターン見えてみました．
なんか同じようなコードばかり書いています．
単調なコーディング
飽きた
辛い

なんとかならないものでしょうか？

__そこで登場するのがRustのderiveという機能です！__

Rustで何らかの型を定義するときに，上に`#[derive(A)]`と書くと，その型にトレイトAが自動実装されるあの機能です．
実はこのRustのderiveを使って，データ型として表現した全ての非終端記号に，`#[derive(Reader)]`と書くだけで，Readerトレイトを実装することができるようになるわけです．
まずは，Rustのderiveの仕組みを眺めてみましょう．

![derive.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/57bb7b03-3be8-b8b9-a0cd-fe74660e7466.png)

一般的なコンパイルの流れはこんな感じです．
1. まずソースコードを字句解析して，字句の列であるトークンストリームを生成する．
1. 次にトークンストリームを構文解析して字句同士の関係性を構文木として表現します．
1. 最後に構文木を意味解析して，マシンコードを生成します．

この流れの中でRustのderiveが動くのは字句解析の後，構文解析の前です．
字句解析されたトークンストリームの中から，deriveが掛かった型定義部分のトークンストリームが掬い取られて，derive関数に入力されます．
derive関数は，deriveが掛かった型定義部分のトークンストリームを入力とし，その型がderiveするトレイトのimplブロックのトークンストリームを出力とします．
そして出力されたトークンストリームが本流のトークンストリームに合流することで，型にトレイトが自動的に実装されるわけです．
deriveによって自作トレイトを自動実装する場合，上の図におけるderive関数は，自分で作る必要があります．

#### derive関数の作り方

derive関数はカーネル本体のクレートとは別のクレートとして作成する必要があります．
[`kernel/Cargo.toml`](https://github.com/TaiseiIto/HeliOS/blob/main/kernel/Cargo.toml)に，`acpi_machine_language`という新しいクレートを作ります．

```toml:kernel/Cargo.toml
[package]
name = "kernel"
version = "0.1.0"
edition = "2021"

[dependencies]
bitfield-struct = "0.5"
naked-function = "0.1.5"

[dependencies.acpi_machine_language]
path = "acpi_machine_language"

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"

[workspace]
members = ["acpi_machine_language"]
```

`workspace`に`acpi_machine_language`という新しいディレクトリを追加した上で，`dependencies`の中に自作のクレート`acpi_machine_language`を追加し，その場所は先ほど`workspace`に追加した`acpi_machine_language`だよ，ということを書いておきます．
そして新しく作成した`acpi_machine_language`クレートに対して，[`kernel/acpi_machine_language/Cargo.toml`](https://github.com/TaiseiIto/HeliOS/blob/main/kernel/acpi_machine_language/Cargo.toml)を配置します．

```toml:kernel/acpi_machine_language/Cargo.toml
[package]
name = "acpi_machine_language"
version = "0.1.0"
edition = "2021"

[dependencies]
proc-macro2 = "1.0.73"
quote = "1.0.34"

[dependencies.syn]
version = "2.0.44"
features = ["extra-traits"]

[lib]
proc-macro = true
```

`proc-macro2`やら`quote`やら`syn`というのは，deriveを自作するときに使うお決まりのライブラリです．
そして，[`kernel/acpi_machine_language/src/lib.rs`](https://github.com/TaiseiIto/HeliOS/blob/main/kernel/acpi_machine_language/src/lib.rs)に，derive関数を記述します．

```Rust:derive関数
#[proc_macro_derive(Reader)]
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    ...
}
```

`#[proc_macro_derive(Reader)]`と書くことによって，カーネル本体のソースコードにおいて型定義に`#[derive(Reader)]`と書かれると，その型定義部分のトークンストリームを入力としてこの関数が呼び出され，その型が`Reader`トレイトを実装するimplブロックのトークンストリームが出力されるという仕組みです．

#### derive関数の内部でやること

まずは入力された型定義部分のトークンストリームを構文解析します．

```Rust:入力された型定義部分のトークンストリームを構文解析
#[proc_macro_derive(Reader)]
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let derive_input: DeriveInput = parse(input).unwrap();
    ...
}
```

入力された型定義部分のトークンストリームを`parse`して`unwrap`すると，`DeriveInput`型という構文木が得られます．
[`DeriveInput`型の定義](https://docs.rs/syn/latest/syn/struct.DeriveInput.html)を見てみましょう．

```Rust:DeriveInput型
pub struct DeriveInput {
    pub attrs: Vec<Attribute>,
    pub vis: Visibility,
    pub ident: Ident,
    pub generics: Generics,
    pub data: Data,
}
```

各フィールドは以下の通りです．

* `attrs`は`#[derive]`や`#[repr]`といった，型に付加された属性の構文木
* `vis`はpubが付いているかどうかとか
* `ident`は型名
* `generics`は型引数やライフタイムパラメータ部分の構文木
* `data`は型の中身の構文木

型の中身の構文木[`Data`](https://docs.rs/syn/latest/syn/enum.Data.html)を見てみましょう．

```Rust:型の中身の構文木Data型
pub enum Data {
    Struct(DataStruct),
    Enum(DataEnum),
    Union(DataUnion),
}
```

各値は以下の通りです．

* `Struct`は構造体の構文木
* `Enum`は列挙体の構文木
* `Union`は共用体の構文木

構造体の構文木[`DataStruct`](https://docs.rs/syn/latest/syn/struct.DataStruct.html)を見てみましょう．

```Rust:構造体の構文木DataStruct型
pub struct DataStruct {
    pub struct_token: Struct,
    pub fields: Fields,
    pub semi_token: Option<Semi>,
}
```

各フィールドは以下の通りです．

* `struct_token`は構造体であることを示す`struct`という字句
* `fields`は構造体の中身のフィールド一覧の構文木
* `semi_token`は構造体の定義の最後に付くセミコロンの字句

構造体の中身のフィールド一覧[`Fields`](https://docs.rs/syn/latest/syn/enum.Fields.html)を見てみましょう．

```Rust:構造体の中身のフィールド一覧の構文木Fields型
pub enum Fields {
    Named(FieldsNamed),
    Unnamed(FieldsUnnamed),
    Unit,
}
```

各値は以下の通りです．

* `Named`は`struct {...}`で囲まれた名前付きフィールド一覧の構文木
* `Unnamed`は`struct (...)`で囲まれた名前なしフィールド一覧の構文木
* `Unit`はフィールドをひとつも持たない構造体の構文木

名前付きフィールド一覧の構文木[`FieldsNamed`](https://docs.rs/syn/latest/syn/struct.FieldsNamed.html)を見てみましょう．

```Rust:名前付きフィールド一覧
pub struct FieldsNamed {
    pub brace_token: Brace,
    pub named: Punctuated<Fields, Comma>,
}
```

各フィールドは以下の通りです．

* `brace_token`はフィールド一覧を囲む括弧の字句
* `named`はフィールドの構文木の列で，`.iter()`メソッドで各フィールドの構文木`&Field`を出すイテレータが得られる

全部見て行くときりがないのでここまでにしておきますが，このように`#[derive(Reader)]`が掛けられた型定義の構文木が`syn`クレートに従って表現されるわけです．
また，Rustの構文木を調べる方法として，`syn`クレートの他に[AST Explorer](https://astexplorer.net/)を使うのも便利です．
AST Explorerはいろんな言語を構文解析してくれるサイトで，以下の画像のように言語としてRustを選択し，左側の入力欄にRustのコードを入力すると，右側に構文木を示してくれます．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/359096d7-ada8-d8ad-c0c3-02883551c4f6.png)

AST Explorerで`#[derive(Reader)]`が掛けられた型定義の構文木の構造を把握し，`syn`クレートでその構造に従って構文木を解析しつつその型が`Reader`トレイトを実装するimplブロックを構築していくコードを書いていきます．
derive関数におけるコードの構築方法はこんな感じです．

```Rust:derive関数におけるコード構築の雰囲気
let ident: Ident = ...;
let read: proc_macro2::TokenStream = ...;
let matches: proc_macro2::TokenStream = ...;
let reader: proc_macro2::TokenStream = quote! {
    impl Reader for #ident {
        fn read(aml: &[u8]) -> (Self, &[u8]) {
            #read
        }

        fn matches(aml: &[u8]) -> bool {
            #matches
        }
    }
};
```

型名などの識別子は，`Ident`型として表現されます．
トークンストリームは`proc_macro2::TokenStream`型として表現されます．
上のコードでは，`Reader`トレイトを実装する型の型名を`ident`に入れ，`read`関数の中身のコードを構築して`read`に入れ，`matches`関数の中身のコードを構築して`matches`に入れています．
最後にそれらを`reader`としてまとめています．
コード構築の際には`quote!`マクロを使用します．
単純に`quote!`マクロの中に書いたコードがトークンストリームに変換されます．
`quote!`マクロの中に`#ident`や`#read`や`#matches`と書かれていますが，これによって`quote!`マクロの中に上のlet文で作成しておいた識別子やトークンストリームを埋め込むことができます．
その他，書式で識別子を構築する`format_ident!`マクロや，トークンストリームの配列`tokenstreams`を指定した区切り`delimiter`で結合する`#(#tokenstreams)delimiter*`という書き方も存在します．
詳細は[derive関数の完全なソースコードは](https://github.com/TaiseiIto/HeliOS/blob/main/kernel/acpi_machine_language/src/lib.rs)を参照してください．

#### derive関数へのパラメータ渡し

また，deriveによってある型にトレイトを実装させる際に，derive関数に何らかのパラメータを渡すことによって実装方法を調整することもできます．
例えばAMLの非終端記号に`<ExtOpPrefix>`があり，その生成規則は

```
<ExtOpPrefix> ::= 0x5b
```

です．
これは型としては

```Rust
pub struct ExtOpPrefix;
```

と表現されますが，これに対するmatches関数は

```Rust
fn matches(aml: &[u8]) -> bool 
    aml.first().is_some_and(|byte| *byte == 0x5b)
}
```

である必要があります．
しかし，derive関数へ入力される

```Rust
#[derive(Reader)]
pub struct ExtOpPrefix;
```

というコードには，肝心の`0x5b`という値が含まれておらず，これではderive関数は非終端記号`ExtOpPrefix`のmatches関数において何の値と比較すればよいのかわかりません．
そこで，

```Rust
#[derive(Reader)]
#[encoding_value = 0x5b]
pub struct ExtOpPrefix;
```

のようにパラメータを記載すると，derive関数への入力であるDeriveInput型

```Rust:DeriveInput型
pub struct DeriveInput {
    pub attrs: Vec<Attribute>,
    pub vis: Visibility,
    pub ident: Ident,
    pub generics: Generics,
    pub data: Data,
}
```

のattrsの中から`#[encoding_value = 0x5b]`の行の構文木を取り出し，正しいmatches関数を構築することができるようになるわけです．

このようにAMLの非終端記号の定義だけを書いておけば，`#[derive(Reader)]`によって構文解析を行うimplブロックを自動生成できるようになります．

### AML構文解析のラスボス，MethodInvocation

AMLの非終端記号の中でも特に構文解析が困難なものがあります．
それは関数呼び出しを意味する非終端記号`<MethodInvocation>`です．
生成規則は

```
<MethodInvocation> ::= <NameString><TermArgList>
<TermArgList> ::= ε | <TermArg><TermArgList>
```

となっています．
`<NameString>`は呼び出す関数名，`<TermArg>`は呼び出す関数に渡される引数で，`<TermArgList>`は0個以上の引数の列を意味します．
`<MethodInvocation>`の問題点は，引数の個数の情報がな，AMLのバイト列のうち，どこまでをTermArgListとして解釈すればよいのか分からないことです．

![method_invocation.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/9b2b14b2-639b-4d2a-1e0b-c8a6ffae5206.png)

#### 可変長の非終端記号のバイト数を示す非終端記号`<PkgLengthLength>`

実は，`<MethodInvocation>`以外にも可変長の非終端記号はたくさんあります．
例えば以下の生成規則を見てみましょう．

```
<DefScope> ::= <ScopeOp><PkgLength><NameString><TermList>
<TermList> ::= ε | <TermObj><TermList>
```

これだけ見ると，`<TermList>`の中にいくつ`<TermObj>`があるのか分かりません．
その情報は，`<PkgLength>`が持っています．
`<PkgLength>`を意味解析すると自然数が得られ，その自然数はAMLのバイト列における`<PkgLength>`の先頭から`<TermList>`の末尾までのバイト数を示しています．
つまり，`<PkgLength>`が100という値を表していたならば，`<TermObj>`をひとつずつ読んでいき，`<PkgLength>`の先頭から100バイト目の位置に到達したら，そこが`<TermList>`の終端であると判定できるのです．

![def_scope.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/430caa50-886c-ddf4-7cf9-d793c754e58e.png)

AMLにおける可変長の非終端記号を構文解析する方法が分かったところで，もう一度`<MethodInvocation>`の生成規則を見てみましょう．

```
<MethodInvocation> ::= <NameString><TermArgList>
<TermArgList> ::= ε | <TermArg><TermArgList>
```

__なんで`<PkgLength>`持たせなかったの？？？__

これは30日でできるOS自作入門風に言えば完全にインテルの人に聞いてください案件です．（ACPIはインテルだけでなく東芝やマイクロソフトも関わっています．）
ただし何の手掛かりもないわけではありません．
関数呼び出しがあるということは，当然関数定義も存在するわけです．
AMLにおける関数定義は，非終端記号`<DefMethod>`で表され，以下のような生成規則を持ちます．

```
<DefMethod> ::= <MethodOp><PkgLength><NameString><MethodFlags><TermList>
```

* `<MethodOp>`は，「これから関数定義が始まるよ」というしるし
* `<PkgLength>`は，自信の先頭から`<TermList>`の終端までのバイト数
* `<NameString>`は，関数名
* `<MethodFlags>`は，固定長1バイトで，__下位3ビットが引数の個数__
* `<TermList>`は，関数内の命令列

`<MethodFlags>`が引数の個数の情報を持っています．
つまり，AMLを構文解析する際に，DefMethodの引数の個数の情報を拾いながら読み進めていけば，`<MethodInvocation>`の引数の個数が分かるわけです．

ただし，いくつか注意点があります．

#### 注意点その１：関数定義よりも前に関数呼び出しがある場合がある．

![invocation_before_define.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/675b0baa-9679-fa38-58b2-e64b2898f3f4.png)

上の図の関数fは，先に関数fの定義が記述され，その後関数fの呼び出しが記述されています．
AMLを前から順番に読んでいけば，関数fの定義で関数fの引数が2個であることがわかり，関数fの呼び出しを構文解析するときに引数が2個であることがわかっているので，問題なく構文解析できます．
一方，関数gに関しては，先に関数gの呼び出しが記述され，その後に関数gの定義が記述されています．
AMLの構文解析は先頭から順番に読んでいき，読み飛ばしはできません．
関数gの呼び出しを構文解析しようとしても，それは構文解析器によっては未定義の関数であり，引数の個数はわかりません．

どうしたものか...

ここであまり自信のない仮説を思いつきました．
それは，
__関数呼び出しは，関数の中にしか現れない__
という仮説です．
もしこの仮説が正しければ，こんなことが可能になります．

![twice_readings.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/7fc78929-c349-b67d-dd7c-6c6908ae0a70.png)

構文解析を，２回に分けます．
１回目の構文解析では，関数定義に出会ったときに，引数の個数の情報だけ取って，中身の命令列は解析しません．
仮説が正しければ，関数呼び出しに出会わなくて済みます．
そして，２回目の構文解析で，改めて関数の中身の命令列を構文解析します．
１回目の構文解析で全ての関数の引数の個数を得られるので，この段階では関数呼び出しも構文解析することができます．

この仮説は真と言えるでしょうか？
残念ながら，構文レベルでは関数の外で関数を呼び出すことは禁止されていません．
なぜならば，関数定義`<DefMethod>`を経由することなく関数呼び出し`<MethodInvocation>`に至る経路が存在するからです．

```:関数定義を経由することなく関数呼び出しに至る経路
<AMLCode> ::= <DefBlockHeader><TermList>
<TermList> ::= ε | <TermObj><TermList>
<TermObj> ::= <Object> | <StatementOpcode> | <ExpressionOpcode>
<ExpressionOpcode> ::= .. | <MethodInvocation> | ..
```

しかし，私がHeliOSの動作確認に使用しているQEMU，VirtualBox，VMware，GPD MicroPCでは仮説は真でした．
動作確認用の全環境でこの仮説が真であることが確認できたので，この仮説を採用することにしました．

#### 注意点その２：名前空間

AMLは名前空間`<DefScope>`及びデバイス`<DefDevice>`からなる意味的な階層構造を規定します．
例えば下の図において，最上位の名前空間`\`の下に，２つの名前空間`SA`と`SB`があります．
`SA`の下にはデバイス`MEM`があり，デバイスに対する書き込み関数`SET`と読み込み関数`GET`があります．
`SB`の下には２つのデバイス`MEM`と`CPU`があり，それぞれのデバイスに対する書き込み関数`SET`と読み込み関数`GET`があります．

![scope.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/b3053a2c-7918-70ea-0311-5c7970fae46d.png)

異なる名前空間，異なるデバイスに存在する同名の関数は，引数の個数が違うかもしれないので，ちゃんと区別する必要があります．
そのために，１回目の構文解析で，上の図のような名前木（と呼ぶことにする）を構築する必要があります．
また，構文木と名前木は形が違うことがあります．
これはどういうことかというと，以下のようなASLコードを考えてみましょう．

```:構文木と名前木の形が違う例
Scope (_SB)
{
    Device (PCI0)
    {
        Method (^BN00, 0, NotSerialized)
        {
            ...
        }
    }
}
```

名前空間`_SB`の中にデバイス`PCI0`があり，デバイス`PCI0`の中に関数`BN00`があるため，この関数の名前木上のパスも`_SB.PCI0.BN00`になるように思われますが，実は関数名`^BN00`の`^`が親を意味しているため，この関数はデバイス`PCI0`の中ではなく，名前空間`_SB`の直下にあります．

#### 注意点その３：外部参照

外部参照は非終端記号`<DefExternal>`で表され，以下のような生成規則を持ちます．

```
<DefExternal> ::= <ExternalOp><NameString><ObjectType><ArgumentCount>
```

SSDTなど外部で定義されている関数のextern宣言のようなもので，`<ArgumentCount>`が引数の個数なので，この情報も持っておく必要があります．

#### 注意点その４：エイリアス

エイリアスは非終端記号`<DefAlias>`で表され，以下のような生成規則を持ちます．

```
<DefAlias> ::= <AliasOp><NameString><NameString>
```

１つ目の`<NameString>`は，もともと存在する名前を表し，２つ目の`<NameString>`は，新しく付けられる名前を表します．
もともと存在する名前が相対パスである場合，その相対パスの起点は`<DefAlias>`が呼び出された場所ではなく，`<DefAlias>`が存在する場所であることに注意する必要があります．
関数がエイリアスで呼び出されることも当然あるので，そういう時なもとの名前を辿って適切に引数の個数を取得できるようにする必要があります．

#### 注意点その５：「常識」の存在

名前がアンダースコアで始まる関数は，機種に依存することなく役割や機能が決まっています．
ACPIの仕様書の5.6.8や5.7に，そこら辺の情報が載っています．
引数の個数も暗黙に定められているので，調べておく必要があります．

以上の注意点を踏まえた上で，関数の引数の個数を取得する仕組みをRustで書いてみましょう．
まず名前木上のノードの住所を意味するパスを表現します．
ソースコードは[こちら](https://github.com/TaiseiIto/HeliOS/blob/main/kernel/src/acpi/machine_language/name.rs)

```Rust:パスの表現
pub enum Segment {
    Child {
        name: String,
    },
    Parent,
    Root,
}

pub struct Path {
    segments: VecDeque<Segment>,
}

pub struct AbsolutePath {
    current: Path,
    relative: Path,
}
```

`Segment`は，子ノード`Child`，親ノード`Parent`，根ノード`Root`を表現できる列挙体です．
`Path`は，`Segment`の列です．
`AbsolutePath`は，現在地`current`と相対パス`relative`から絶対パスを求めるための構造体です．

次に名前木のノードを表現します．

```Rust:名前木のノードの表現
pub struct Node {
    name: Segment,
    object: Object,
    children: Vec<Self>,
}

pub enum Object {
    Alias {
        original_path: AbsolutePath,
    },
    CreateBitField,
    CreateByteField,
    CreateDWordField,
    CreateField,
    CreateQWordField,
    CreateWordField,
    DataRegion,
    Device,
    Event,
    External {
        number_of_arguments: usize,
    },
    Load,
    Method {
        number_of_arguments: usize,
    },
    Mutex,
    Name,
    NamedField,
    OpRegion,
    PowerRes,
    Processor,
    Scope,
    ThermalZone,
}
```

AMLで定義されるあらゆるものを列挙する列挙型`Object`を作っておいて，名前木を構成するノードを`Node`という構造体として表現します．
`Node`は，名前を`segment`として持ち，ノードの種類を`object`として持ち，子ノードの集合を`children`として持ちます．

パスや名前木を表現できるようになったところで，１回目の解析と２回目の解析を実装していきます．
解析を２段階に分けるにあたって，まずは関数定義の非終端記号`<DefMethod>`に少し細工をします．
`<DefMethod>`の生成規則

```
<DefMethod> ::= <MethodOp><PkgLength><NameString><MethodFlags><TermList>
```

に対応するデータ型として

```Rust
pub struct DefMethod(
    MethodOp,
    PkgLength,
    NameString,
    MethodFlags,
    MethodTermList,
);

pub enum MethodTermList {
    Binary(ByteList),
    SyntaxTree(TermList),
}
```

を定義します．
`MethodTermList`が関数の中身の命令列を意味しますが，１回目の解析では`Binary`としてバイト列のまま持っておき，２回目の解析でそれを構文解析して`SyntaxTree`に変換します．

１回目の解析および２回目の解析を以下のようにトレイトとして表現します．
ソースコードは[こちら](https://github.com/TaiseiIto/HeliOS/blob/main/kernel/src/acpi/machine_language/syntax.rs)

```Rust
pub trait FirstReader {
    fn first_read<'a>(aml: &'a [u8], root: &mut semantics::Node, current: &semantics::Path) -> (Self, &'a [u8]) where Self: Sized;
}

pub trait ReaderOutsideMethod {
    fn read_outside_method(&mut self, root: &mut semantics::Node, current: &semantics::Path);
}

pub trait ReaderInsideMethod {
    fn read_insize_method<'a>(aml: &'a [u8], root: &mut semantics::Node, current: &semantics::Path) -> (Self, &'a [u8]) where Self: Sized;
}
```

* `FirstReader`トレイトは，１回目の解析を行います．
    * `first_read`関数は，関数外の構文解析を行い，関数内の命令列の構文解析は行わず，バイナリのまま持っておきます．
        * 第一引数amlは，解析対象のAMLです．
        * 第二引数rootは名前木で，解析しながらノードを追加していきます．
        * 第三引数curerntは名前木上の現在地です．
        * 返り値は構文解析済みの非終端記号と，AMLお未解析の部分です．
* `ReaderOutsideMethod`トレイトは，２回目の解析における関数の外側の解析を行います．
    * `read_outside_method`関数は，基本的には自身の直下の非終端記号の`read_outside_method`関数を再帰的に呼び出し，`MethodTermList`があったら，`read_inside_method`関数を呼び出します．
        * 第一引数selfは非終端記号で，自信のは以下の関数定義の中身の命令列をバイナリから構文木に変換するのでmutとしています．
        * 第二引数rootは名前木で，関数定義内のローカル変数が追加されることがあるのでmutとしています．
        * 第三引数currentは名前木上の現在地です．
* `ReaderInsideMethod`トレイトは，２回目の解析における関数の内側の解析を行います．
    * `read_inside_method`関数は，関数定義内の命令列を構文解析し，関数呼び出しに遭遇したら，名前木からその関数の引数の個数を取得して構文解析を続行します．
        * 第一引数amlは解析対象のAMLです．
        * 第二引数rootは名前木で，関数の検索やローカル変数の追加などを行います．
        * 第三引数currentは名前木上の現在位置です．
        * 返り値は構文解析済みの非終端記号と，AMLの未解析の部分です．

これらのトレイトを，deriveを駆使して全ての非終端記号に実装していきます．

メソッドごとにトレイトを分けている理由は，deriveによる自動実装とソースコード直書きによる手動実装を柔軟に切り替えられるようにするためです．
それをやっている例が以下のコードです．

```Rust:部分的な手動実装の例
#[derive(Reader)]
#[manual(matcher)]
pub enum NameString {
    AbsolutePath(
        RootChar,
        NamePath,
    ),
    RelativePath(
        PrefixPath,
        NamePath,
    ),
}

impl Matcher for NameString {
    fn matches(aml: &[u8]) -> bool {
        DualNamePath::matches(aml)
        || MultiNamePath::matches(aml)
        || NameSeg::matches(aml)
        || ParentPrefixChar::matches(aml)
        || RootChar::matches(aml)
    }
}
```

この例では，非終端記号`<NameString>`に`Reader`トレイトを自動実装していますが，`#[manual(Matcher)]`と指定することで，`Reader`トレイトから`matches`関数だけを分離させた`Matcher`トレイトだけは手動実装しています．
このようにトレイトをメソッドごとに分離させることで，メソッドごとに自動実装と手動実装を切り替えることができます．

ここで，AMLの構文解析における２回読みの全体的な流れを確認しましょう．
まずは`FirstReader`トレイトによる１回目の解析です．
AMLを読みながら，構文木と名前木を同時に構築していきます．

![FirstReader.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/b8207204-056a-2700-dcb2-157662974c2f.png)

関数内の命令列は，解析せずにバイナリのまま持っておきます．

![FirstReaderMethodInvocation.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/b5af2310-7d3e-2488-42c7-c1cd7d1ad411.png)

次に`ReaderOutsideMethod`トレイトによる２回目の解析です．
１回目の解析で得られた構文解析に対して，深さ優先探索の順番で`read_outside_method`を再帰呼び出ししていきます．

![ReaderOutsideMethod.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/8a46c400-daff-fa1a-df6f-37dfebcc82f0.png)

１回目の解析で解析せずにバイナリのまま持っておいた関数内の命令列`MethodTermList`に遭遇したら，`ReaderInsideMethod`トレイトの`read_inside_method`関数を呼び出します．
`ReaderInsideMethod`トレイトは，関数内の命令列を構文解析していきます．

![ReaderInsideMethod.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/1150c7bc-d889-8c65-e9ed-d2a31ea333e4.png)

関数呼び出しに遭遇したら，名前木からその関数を探し出して引数の個数を取得し，構文解析を続行します．

![ReaderInsideMethodInvocation.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/accc9ce4-dfb1-0091-928a-a13d9799aadc.png)

これでようやくAMLを構文解析できるようになりました！

## AMLの実行

さて，電源状態を遷移する手順は，[ACPI Specification](https://uefi.org/sites/default/files/resources/ACPI_Spec_6_5_Aug29.pdf)の7.5に記載されています．

[ACPI Specification](https://uefi.org/sites/default/files/resources/ACPI_Spec_6_5_Aug29.pdf)図7.1
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/6eb25a34-05f7-c179-5a2f-ce0e8cb2bf58.png)

電源状態S5に遷移する方法は，以下の通りです．

* `\_TTS(5)`を実行
* `\_PTS(5)`を実行
* `\_S5`に記述された値を`PM1a_CNT_BLK`および`PM1b_CNT_BLK`に書き込む

関数`\_TTS`，関数`\_PTS`，値`\_S5`は，全てDSDTにAMLで記述されています．
関数`\_TTS`と関数`\_PTS`に関しては存在しない場合がありますが，その場合はその関数を実行する必要はありません．
DSDTの構文木の中から，その部分木として存在する関数`\_TTS`，関数`\_PTS`，値`\_S5`を探し出しましょう．

### 構文木の地図を作る

先程も説明したように，AMLは構文木の形と意味上の木の形が異なる場合があります．
そうなると`\_TTS`や`\_PTS`といったパスが与えられたとき，それに対応する構文木上の部分木がどこにあるかわからないので結局構文木全体を探し回る必要があります．
さらに，`\_TTS`や`\_PTS`といった関数からさらに別の関数が呼び出されることもあります．
そのたびに呼び出す関数を構文木から探していては，実行速度はかなり遅くなるでしょう．
この問題を解決するために，下の図のようにパスから構文木の対応部分を参照する参照木をあらかじめ作っておきます．

![reference_tree.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354177/da5bd1f4-3b9c-6ec6-1d2c-7d2012c01e4c.png)

参照木のソースコードは[こちら](https://github.com/TaiseiIto/HeliOS/blob/main/kernel/src/acpi/machine_language/reference.rs)

参照木は以下のノードから構成されます．

```Rust:参照木を構成するノード
pub struct Node<'a> {
    name: name::Segment,
    objects: Vec<Object<'a>>,
    children: Vec<Self>,
}
```

`name`は`\`や`_TTS`といった名前を持ちます．
`objects`はその名前が指し示す構文木上の部分木への参照を持ちます．
`children`は参照木上の子ノードを持ちます．

```Rust:構文木上の部分木への参照
pub enum Object<'a> {
    Alias(&'a syntax::DefAlias),
    CreateBitField(&'a syntax::DefCreateBitField),
    CreateByteField(&'a syntax::DefCreateByteField),
    CreateDWordField(&'a syntax::DefCreateDWordField),
    CreateField(&'a syntax::DefCreateField),
    CreateQWordField(&'a syntax::DefCreateQWordField),
    CreateWordField(&'a syntax::DefCreateWordField),
    DataRegion(&'a syntax::DefDataRegion),
    Device(&'a syntax::DefDevice),
    Event(&'a syntax::DefEvent),
    External(&'a syntax::DefExternal),
    Load(&'a syntax::DefLoad),
    Method(&'a syntax::DefMethod),
    Mutex(&'a syntax::DefMutex),
    Name(&'a syntax::DefName),
    NamedField {
        access_type: interpreter::AccessType,
        named_field: &'a syntax::NamedField,
        offset_in_bits: usize,
        op_region: name::Path,
    },
    OpRegion(&'a syntax::DefOpRegion),
    PowerRes(&'a syntax::DefPowerRes),
    Processor(&'a syntax::DefProcessor),
    Scope(&'a syntax::DefScope),
    ThermalZone(&'a syntax::DefThermalZone),
}
```

構文木上の部分木への参照である`Object`列挙型は，各種構文を表す非終端記号への参照を持ちます．

### AMLのデータ型

AMLで表現しうるすべてのデータ型を列挙する列挙型`Value`を定義します．
ソースコードは[こちら](https://github.com/TaiseiIto/HeliOS/blob/main/kernel/src/acpi/machine_language/interpreter.rs)

```Rust:AMLで表現しうるすべてのデータ型
pub enum Value {
    Bool(bool),
    Buffer(Vec<u8>),
    Byte(u8),
    Char(char),
    DWord(u32),
    One,
    Ones,
    Package(Vec<Self>),
    QWord(u64),
    String(String),
    Word(u16),
    Zero,
}
```

それぞれのデータ型は以下のような意味を持ちます．

* `Bool`は真理値
* `Buffer`はバイト列
* `Byte`は8ビット整数
* `Char`は文字
* `DWord`は32ビット整数
* `One`は最下位ビットのみが1であるという情報のみを持ち，自身のビット数を定めない値
* `Ones`は全ビットが1であるという情報のみを持ち，自身のビット数を定めない値
* `Package`は複数の値をまとめたタプル
* `QWord`は64ビット整数
* `String`は文字列
* `Word`は16ビット整数
* `Zero`は全ビットが0であるという情報のみを持ち，自身のビット数を定めない値

値を表現できるようになったところで，次は値に対する演算を実装します．
以下に一部の演算の実装コードを掲載します．

```Rust:２つの値の型を合わせるキャスト
fn match_type(&self, other: &Self) -> (Self, Self) {
    match (self, other) {
        (Self::Bool(left), Self::Bool(right)) => (Self::Bool(*left), Self::Bool(*right)),
        (Self::Buffer(left), Self::Buffer(right)) => (Self::Buffer(left.clone()), Self::Buffer(right.clone())),
        (Self::Buffer(left), Self::String(right)) => {
            let right: Vec<u8> = right
                .bytes()
                .chain(iter::repeat(0x00))
                .take(left.len())
                .collect();
            (Self::Buffer(left.clone()), Self::Buffer(right))
        },
        (Self::Byte(left), Self::Byte(right)) => (Self::Byte(*left), Self::Byte(*right)),
        (Self::Byte(left), Self::DWord(right)) => (Self::DWord(*left as u32), Self::DWord(*right)),
        (Self::Byte(left), Self::One) => (Self::Byte(*left), Self::Byte(0x01)),
        (Self::Byte(left), Self::Ones) => (Self::Byte(*left), Self::Byte(0xff)),
        (Self::Byte(left), Self::QWord(right)) => (Self::QWord(*left as u64), Self::QWord(*right)),
        (Self::Byte(left), Self::Word(right)) => (Self::Word(*left as u16), Self::Word(*right)),
        (Self::Byte(left), Self::Zero) => (Self::Byte(*left), Self::Byte(0x00)),
        (Self::Char(left), Self::Char(right)) => (Self::Char(*left), Self::Char(*right)),
        (Self::DWord(left), Self::Byte(right)) => (Self::DWord(*left), Self::DWord(*right as u32)),
        (Self::DWord(left), Self::DWord(right)) => (Self::DWord(*left), Self::DWord(*right)),
        (Self::DWord(left), Self::One) => (Self::DWord(*left), Self::DWord(0x00000001)),
        (Self::DWord(left), Self::Ones) => (Self::DWord(*left), Self::DWord(0xffffffff)),
        (Self::DWord(left), Self::QWord(right)) => (Self::QWord(*left as u64), Self::QWord(*right)),
        (Self::DWord(left), Self::Word(right)) => (Self::DWord(*left), Self::DWord(*right as u32)),
        (Self::DWord(left), Self::Zero) => (Self::DWord(*left), Self::DWord(0x00000000)),
        (Self::One, Self::Byte(right)) => (Self::Byte(0x01), Self::Byte(*right)),
        (Self::One, Self::DWord(right)) => (Self::DWord(0x00000001), Self::DWord(*right)),
        (Self::One, Self::One) => (Self::One, Self::One),
        (Self::One, Self::Ones) => (Self::One, Self::Ones),
        (Self::One, Self::QWord(right)) => (Self::QWord(0x0000000000000001), Self::QWord(*right)),
        (Self::One, Self::Word(right)) => (Self::Word(0x0001), Self::Word(*right)),
        (Self::One, Self::Zero) => (Self::One, Self::Zero),
        (Self::Ones, Self::Byte(right)) => (Self::Byte(0xff), Self::Byte(*right)),
        (Self::Ones, Self::DWord(right)) => (Self::DWord(0xffffffff), Self::DWord(*right)),
        (Self::Ones, Self::One) => (Self::Ones, Self::One),
        (Self::Ones, Self::Ones) => (Self::Ones, Self::Ones),
        (Self::Ones, Self::QWord(right)) => (Self::QWord(0xffffffffffffffff), Self::QWord(*right)),
        (Self::Ones, Self::Word(right)) => (Self::Word(0xffff), Self::Word(*right)),
        (Self::Ones, Self::Zero) => (Self::Ones, Self::Zero),
        (Self::Package(left), Self::Package(right)) => (Self::Package(left.clone()), Self::Package(right.clone())),
        (Self::QWord(left), Self::Byte(right)) => (Self::QWord(*left), Self::QWord(*right as u64)),
        (Self::QWord(left), Self::DWord(right)) => (Self::QWord(*left), Self::QWord(*right as u64)),
        (Self::QWord(left), Self::One) => (Self::QWord(*left), Self::QWord(0x0000000000000001)),
        (Self::QWord(left), Self::Ones) => (Self::QWord(*left), Self::QWord(0xffffffffffffffff)),
        (Self::QWord(left), Self::QWord(right)) => (Self::QWord(*left), Self::QWord(*right)),
        (Self::QWord(left), Self::Word(right)) => (Self::QWord(*left), Self::QWord(*right as u64)),
        (Self::QWord(left), Self::Zero) => (Self::QWord(*left), Self::QWord(0x0000000000000000)),
        (Self::Revision, Self::Revision) => (Self::Revision, Self::Revision),
        (Self::String(left), Self::String(right)) => (Self::String(left.clone()), Self::String(right.clone())),
        (Self::Word(left), Self::Byte(right)) => (Self::Word(*left), Self::Word(*right as u16)),
        (Self::Word(left), Self::DWord(right)) => (Self::DWord(*left as u32), Self::DWord(*right)),
        (Self::Word(left), Self::One) => (Self::Word(*left), Self::Word(0x0001)),
        (Self::Word(left), Self::Ones) => (Self::Word(*left), Self::Word(0xffff)),
        (Self::Word(left), Self::QWord(right)) => (Self::QWord(*left as u64), Self::QWord(*right)),
        (Self::Word(left), Self::Word(right)) => (Self::Word(*left), Self::Word(*right)),
        (Self::Word(left), Self::Zero) => (Self::Word(*left), Self::Word(0x0000)),
        (Self::Zero, Self::Byte(right)) => (Self::Byte(0x00), Self::Byte(*right)),
        (Self::Zero, Self::DWord(right)) => (Self::DWord(0x00000000), Self::DWord(*right)),
        (Self::Zero, Self::One) => (Self::Zero, Self::One),
        (Self::Zero, Self::Ones) => (Self::Zero, Self::Ones),
        (Self::Zero, Self::QWord(right)) => (Self::QWord(0x0000000000000000), Self::QWord(*right)),
        (Self::Zero, Self::Word(right)) => (Self::Word(0x0000), Self::Word(*right)),
        (Self::Zero, Self::Zero) => (Self::Zero, Self::Zero),
        (left, right)  => unimplemented!("left = {:#x?}, right = {:#x?}", left, right),
    }
}
```

```Rust:足し算
impl Add for Value {
    type Output = Self;

    fn add(self, other: Self) -> Self::Output {
        match self.match_type(&other) {
            (Self::Byte(left), Self::Byte(right)) => Self::Output::Byte(left + right),
            (Self::Word(left), Self::Word(right)) => Self::Output::Word(left + right),
            (Self::DWord(left), Self::DWord(right)) => Self::Output::DWord(left + right),
            (Self::QWord(left), Self::QWord(right)) => Self::Output::QWord(left + right),
            (Self::Zero, Self::Zero) => Self::Output::Zero,
            (Self::Zero, Self::One) => Self::Output::One,
            (Self::Zero, Self::Ones) => Self::Output::Ones,
            (Self::One, Self::Zero) => Self::Output::One,
            (Self::One, Self::One) => Self::Output::QWord(1 + 1),
            (Self::Ones, Self::Zero) => Self::Output::Ones,
            (left, right) => unimplemented!("left = {:#x?}, right = {:#x?}", left, right),
        }
    }
}
```

```Rust:排他的論理和のビット演算
impl BitXor for Value {
    type Output = Self;

    fn bitxor(self, other: Self) -> Self::Output {
        match self.match_type(&other) {
            (Self::Byte(left), Self::Byte(right)) => Self::Output::Byte(left ^ right),
            (Self::Word(left), Self::Word(right)) => Self::Output::Word(left ^ right),
            (Self::DWord(left), Self::DWord(right)) => Self::Output::DWord(left ^ right),
            (Self::QWord(left), Self::QWord(right)) => Self::Output::QWord(left ^ right),
            (Self::Zero, Self::Zero) => Self::Output::Zero,
            (Self::Zero, Self::One) => Self::Output::One,
            (Self::Zero, Self::Ones) => Self::Output::Ones,
            (Self::One, Self::Zero) => Self::Output::One,
            (Self::One, Self::One) => Self::Output::Zero,
            (Self::One, Self::Ones) => Self::Output::QWord(1 ^ u64::MAX),
            (Self::Ones, Self::Zero) => Self::Output::Ones,
            (Self::Ones, Self::One) => Self::Output::QWord(u64::MAX ^ 1),
            (Self::Ones, Self::Ones) => Self::Output::Zero,
            (left, right) => unimplemented!("left = {:#x?}, right = {:#x?}", left, right),
        }
    }
}
```

他にもいくつかの演算を実装しています．
詳細は[ソースコード](https://github.com/TaiseiIto/HeliOS/blob/main/kernel/src/acpi/machine_language/interpreter.rs)を参照してください．

### AMLのスタックフレーム

他のプログラミング言語がそうであるように，AMLも関数を実行するためのスタックフレームを必要とします．

```Rust:AMLのスタックフレーム
pub struct StackFrame {
    arguments: [Option<Value>; 0x07],
    broken: Vec<bool>,
    continued: Vec<bool>,
    locals: [Option<Value>; 0x08],
    named_locals: BTreeMap<name::Path, Value>,
    return_value: Option<Value>,
}
```

* `arguments`は，現在実行中の関数に渡された引数
* `broken`は，while文実行中にbreak命令が実行されたかどうかを表す真理値
* `continued`は，while文実行中にcontinue命令が実行されたかどうかを表す真理値
* `locals`は，現在実行中の関数のローカル変数
* `named_locals`は，現在実行中の関数の名前付きローカル変数
* `return_value`は，現在実行中の関数が返すべき値

### AMLの式の評価

構文木中の式を表す構文に対し，コンテキストを与えてその式を評価し，評価結果を返すEvaluatorトレイトを定義します．

```Rust
pub trait Evaluator {
    fn evaluate(&self, stack_frame: &mut StackFrame, root: &reference::Node, current: &name::Path) -> Option<Value>;
}
```

* `self`は，評価対象となる式を表す構文の非終端記号
* `stack_frame`は，現在実行中の関数のスタックフレーム
* `root`は，参照木の根ノード
* `current`は，参照木上の現在地の絶対パス

`evaluate`関数は，これらの情報をもとに式を評価し，その評価結果を`Option<Value>`として返します．

#### `Evaluator`トレイトの実装例

ここで，Evaluatorトレイトの実装例をいくつが紹介しましょう．

##### １バイトの定数の評価

１バイトの定数を表す構文の非終端記号`ByteData`に対しては，その１バイトの定数を取り出して，`Value`列挙型の`Byte`に入れて返します．

```Rust:１バイトの定数の評価
pub struct ByteData(u8);

impl Evaluator for ByteData {
    fn evaluate(&self, _stack_frame: &mut interpreter::StackFrame, _root: &reference::Node, _current: &name::Path) -> Option<interpreter::Value> {
        let Self(byte) = self;
        Some(interpreter::Value::Byte(*byte))
    }
}
```

##### バイト列の定数の評価

バイト列の定数を表す構文の非終端記号`ByteList`に対しては，そのバイト列から`ByteData`をひとつずつ取り出し，評価し，`Vec<u8>`にまとめ，`Value`列挙型の`Buffer`に入れて返します．

```Rust:バイト列の定数の評価
pub struct ByteList(Vec<ByteData>);

impl Evaluator for ByteList {
    fn evaluate(&self, stack_frame: &mut interpreter::StackFrame, root: &reference::Node, current: &name::Path) -> Option<interpreter::Value> {
        let Self(byte_list) = self;
        Some(interpreter::Value::Buffer(byte_list
            .iter()
            .filter_map(|byte_data| byte_data
                .evaluate(stack_frame, root, current)
                .and_then(|byte_data| byte_data.get_byte()))
            .collect()))
    }
}
```

##### 等式の評価

等式`left == right`を表す構文の非終端記号`DefLEqual`に対しては，両辺をそれぞれ評価し，それらが等しいかどうかを`Value`列挙型の`Bool`に入れて返します．

```Rust:等式の評価
pub struct DefLEqual(
    LEqualOp,
    [Operand; 2],
);

impl Evaluator for DefLEqual {
    fn evaluate(&self, stack_frame: &mut interpreter::StackFrame, root: &reference::Node, current: &name::Path) -> Option<interpreter::Value> {
        let Self(
            _l_equal_op,
            [left, right],
        ) = self;
        let left: Option<interpreter::Value> = left.evaluate(stack_frame, root, current);
        let right: Option<interpreter::Value> = right.evaluate(stack_frame, root, current);
        left
            .zip(right)
            .map(|(left, right)| interpreter::Value::Bool(left == right))
    }
}
```

このように，AMLの式の評価を，その式を表す非終端記号への`Evaluator`トレイトの実装として書いていきます．
式の評価だけではなく，命令列や制御文の実行も`Evaluator`トレイトの実装として作ることができます．

##### 命令列の実行

関数や制御分などのブロック内の命令列を表す非終端記号`TermList`に対しては，命令列`term_objs`を構成する各命令`term_obj`を順番に実行していきます．
ただし，`return`命令，`break`命令，`continue`命令が実行されていないことが継続条件になっています．
これらの命令が実行されたら，そのブロック内のそれ以降の命令はすべて実行されないようにしています．

```Rust:命令列の実行
pub struct TermList(
    Vec<TermObj>,
);

impl Evaluator for TermList {
    fn evaluate(&self, stack_frame: &mut interpreter::StackFrame, root: &reference::Node, current: &name::Path) -> Option<interpreter::Value> {
        let Self(term_objs) = self;
        term_objs
            .iter()
            .for_each(|term_obj| if stack_frame.read_return().is_none() && !stack_frame.is_broken() && !stack_frame.is_continued() {
                term_obj.evaluate(stack_frame, root, current);
            });
        stack_frame
            .read_return()
            .cloned()
    }
}
```

### 代入

非終端記号DefStoreは，「super_name = term_arg」という形式の代入文を表します．
代入文の実行も`Evaluator`トレイトとして実装され，`term_arg`を評価した結果を`super_name`にholdさせています．

```Rust:代入文の評価
pub struct DefStore(
    StoreOp,
    TermArg,
    SuperName,
);

impl Evaluator for DefStore {
    fn evaluate(&self, stack_frame: &mut interpreter::StackFrame, root: &reference::Node, current: &name::Path) -> Option<interpreter::Value> {
        let Self(
            _store_op,
            term_arg,
            super_name,
        ) = self;
        term_arg
            .evaluate(stack_frame, root, current)
            .map(|term_arg| super_name.hold(term_arg, stack_frame, root, current))
    }
}
```

`hold`関数は以下の`Holder`トレイトで定義されています．

```Rust:Holderトレイト
pub trait Holder {
    fn hold(&self, value: Value, stack_frame: &mut StackFrame, root: &reference::Node, current: &name::Path) -> Value;
}
```

各引数の意味は以下の通りです．

* `self`は，代入先を表す構文の非終端記号
* `value`は，代入する値
* `stack_frame`は，現在実行中の関数のスタックフレーム
* `root`は，参照木の根ノード
* `current`は，参照木上の現在地

Holderトレイトの実装例を見てみましょう．

#### ローカル変数への代入

ローカル変数はスタックフレームの中に存在しているので，スタックフレームに対して`*index`番目のローカル変数にvalueを代入させています．

```Rust:ローカル変数への代入
impl Holder for LocalObj {
    fn hold(&self, value: interpreter::Value, stack_frame: &mut interpreter::StackFrame, _root: &reference::Node, _current: &name::Path) -> interpreter::Value {
        let Self(index) = self;
        stack_frame.write_local(*index as usize, value)
    }
}
```

```Rust:スタックフレーム上のローカル変数への書き込み
impl StackFrame {
    ...
    pub fn write_local(&mut self, index: usize, value: Value) -> Value {
        self.locals[index] = Some(value.clone());
        value
    }
    ...
}
```

#### `NamedField`への代入

AMLには，メモリ空間やIO空間の任意の領域を表す構文`NamedField`があります．
`NamedField`の例として，VirtualBoxから取り出したDSDTをAMLからASLに翻訳したものからの抜粋を以下に示します．

```
OperationRegion (DBG0, SystemIO, 0x3000, 0x04)
Field (DBG0, ByteAcc, NoLock, Preserve)
{
    DHE1,   8
}
```

`SystemIO`つまりIO空間の0x3000番ポートから始まる0x04バイトの領域を`DBG0`と名付け，その最初の8ビット分の領域を`DHE1`と名付けています．
AMLインタプリタはAMLを実行していく中でこのようなNamedFieldへの読み書きによってハードウェアとやり取りします．
例えば，`Local0 = DHE1`はIO空間の0x3000番ポートから１バイト読み取って0番目のローカル変数に代入することを意味しますし，`DHE1 = 0`はIO空間の0x3000番ポートに1バイトの0を書き込むことを意味します．

### 制御文の実行

制御文の実行も`Evaluator`トレイトとして実装します．
こちらも例を見てみましょう．

#### if-else文の実行

if-else文は，if文を持ち，オプションでelse文を持つ場合があります．
if文は，条件式`predicate`と中身の命令列`term_list`からなります．
条件式`predicate`を評価して，その結果がtrueだった場合は，if文の中身の命令列`term_list`を実行します．
条件式`predicate`の評価結果がfalseであり，なおかつelse文が存在する場合は，そのelse文を実行します．

```Rust:if-else文の実行
pub struct DefIfElse(
    DefIf,
    Option<DefElse>,
);

impl Evaluator for DefIfElse {
    fn evaluate(&self, stack_frame: &mut interpreter::StackFrame, root: &reference::Node, current: &name::Path) -> Option<interpreter::Value> {
        let Self(
            DefIf(
                _if_op,
                _pkg_length,
                predicate,
                term_list,
            ),
            def_else,
        ) = self;
        if predicate
            .evaluate(stack_frame, root, current)
            .map_or(false, |predicate| (&predicate).into()) {
            term_list.evaluate(stack_frame, root, current)
        } else {
            def_else
                .as_ref()
                .and_then(|def_else| def_else.evaluate(stack_frame, root, current))
        }
    }
}
```

#### while文の実行

while文は条件式`predicate`と中身の命令列`term_list`からなります．
条件式`predicate`の評価結果がtrueであり，break命令が実行されていない限りにおいて，中身の命令列`term_list`を実行し続けます．
また，`term_list`実行中にcontinue命令が実行された場合，`term_list`のそれ以降の命令列は実行されません．
しかし，while文において次のループに進む時には，continue命令が実行されたという記憶をスタックフレームから消しておく必要があるため，`stack_frame.uncontinue()`を実行しています．

```Rust:while文の実行
pub struct DefWhile(
    WhileOp,
    PkgLength,
    Predicate,
    TermList,
);

impl Evaluator for DefWhile {
    fn evaluate(&self, stack_frame: &mut interpreter::StackFrame, root: &reference::Node, current: &name::Path) -> Option<interpreter::Value> {
        let Self(
            _while_op,
            _pkg_length,
            predicate,
            term_list,
        ) = self;
        stack_frame.enter_loop();
        while {
            let predicate: bool = predicate
                .evaluate(stack_frame, root, current)
                .map_or(false, |predicate| (&predicate).into());
            let broken: bool = stack_frame.is_broken();
            predicate && !broken
        } {
            term_list.evaluate(stack_frame, root, current);
            stack_frame.uncontinue();
        }
        stack_frame.leave_loop();
        None
    }
}
```

### 関数呼び出し

関数呼び出しも`Evaluator`トレイトとして実装されます．
まず関数の相対パスを絶対パスに変換しておきます．
次に関数に渡す引数をあらかじめ評価しておきます．
そして，名前付きローカル変数，名前付きグローバル変数，`NamedField`のような関数でないものの評価も，構文上引数の無い関数呼び出しとして扱われるので，ここで評価しています．
本当に関数呼び出しだった場合，その関数を実行するための新しいスタックフレームを作成し，評価しておいた引数の値を入れ，関数を実行し，その返り値を返します．

```Rust:関数呼び出し
pub struct MethodInvocation(
    NameString,
    Vec<TermArg>,
);

impl Evaluator for MethodInvocation {
    fn evaluate(&self, stack_frame: &mut interpreter::StackFrame, root: &reference::Node, current: &name::Path) -> Option<interpreter::Value> {
        let Self(
            name_string,
            term_args,
        ) = self;
        let relative_method_path: name::Path = name_string.into(); // 関数の相対パス
        let absolute_method_path = name::AbsolutePath::new(current, &relative_method_path); // 関数の絶対パス
        let term_args: Vec<interpreter::Value> = term_args // 関数に渡す引数を評価
            .iter()
            .filter_map(|term_arg| term_arg.evaluate(stack_frame, root, current))
            .collect();
        stack_frame
            .read_named_local(&relative_method_path) // 名前付きローカル変数だった場合，その値を返す．
            .or_else(|| root
                .get_name_from_current(&absolute_method_path) // 名前付きグローバル変数名だった場合，その値を返す．
                .and_then(|(current, name)| name.evaluate(stack_frame, root, &current)))
            .or_else(|| root.read_named_field(stack_frame, root, &absolute_method_path)) // NamedFieldだった場合，そこから値を読みだす．
            .or_else(|| root
                .get_method_from_current(&absolute_method_path) // 関数だった場合，その関数を実行し，その返り値を返す．
                .and_then(|(current, method)| {
                    let mut stack_frame = interpreter::StackFrame::default() // 関数を実行するための新しいスタックフレーム
                        .set_arguments(term_args); // 新しいスタックフレームに引数を設定
                    method.evaluate(&mut stack_frame, root, &current) // 関数を実行し，その返り値を返す．
                }))
    }
}
```

長かったですね．
ここまで来ればAMLを実行するとはどういうことか，なんとなくイメージできるようになったのではないでしょうか？
AMLが表現できるすべての演算を実装したわけではありませんが，これまでに説明したような基本的な演算を実装することで，QEMU，VirtualBox，VMware，GPD MicroPCの4環境でシャットダウンできるようになりました．

# まとめ

自作OSにシャットダウンを実装するためには，AMLという言語で書かれたDSDTを解析し，`\_TTS(5)`を実行し，`\_PTS(5)`を実行し，`\_S5`の値を`PM1a_CNT_BLK`および`PM1b_CNT_BLK`に書き込む必要があります．
AMLを構文解析するために，非終端記号を構造体や列挙体などの型て表現し，それらの型に構文解析するトレイトを実装します．
約350種類の非終端記号に対して，それを解析するコードを手動で書くのはかなり労力がいるので，Rustのderiveを使ってトレイトを実装するコードを自動生成します．
構文解析の段階で発生する「関数呼び出しの引数の個数分からない問題」の解決策として，１回目の解析で関数の中身の命令列を解析せずに関数の引数の個数の情報だけ取って，２回目の解析で関数の中身の命令列を解析する「２回読み」があります．
AML上のパスの解決を様にするため，参照木を作り，参照木上でAMLを実行するようにします．
AMLで表現可能なすべてのデータ型を表す列挙型を作り，値同士の四則演算やビット演算などを実装します．
スタックフレームを定義し，AMLの関数実行中のコンテキストを表現します．
非終端記号に`Evaluator`トレイトを実装し，値として評価できるようにします．
変数などの書き込みを行える構文に対しては`Holder`トレイトを実装し，値を保持できるようにします．
AMLは，`NamedField`への読み書きによって，ハードウェアとやり取りします．
if文やwhile文などの制御分の実行も`Evaluator`トレイトとして実装します．
関数呼び出しは，新しいスタックフレームに引数の評価値を入れて実行するようにします．

# 参考文献

* [３０日でできる！OS自作入門](https://book.mynavi.jp/ec/products/detail/id=22078)
* [ゼロからのOS自作入門](https://book.mynavi.jp/ec/products/detail/id=121220)
* [作って理解するOS](https://gihyo.jp/book/2019/978-4-297-10847-2)
* [「作って理解するOS」を読んで「QEMUで電断できねーじゃんか！」になった人を救いたい](https://qiita.com/Egh2Deywos/items/9b3ea5b5bf0046499067)
* [Unified Extensible Firmware Interface (UEFI) Specification Version 2.9](https://uefi.org/sites/default/files/resources/UEFI_Spec_2_9_2021_03_18.pdf)
* [Advanced Configuration and Power Interface (ACPI) Specification Release 6.5](https://uefi.org/sites/default/files/resources/ACPI_Spec_6_5_Aug29.pdf)