mikanOS 2章を整理

# はじめに
いつも好き勝手に書き散らして申し訳ない。
過去に以下のような記事を書いてますが、
今一つ、身についた自覚がなく、悩んでました。

[**1つ目**](https://qiita.com/AKpirion/items/2ed219403a06b3b8c7d5) mikanOS 動作確認に必要な操作をまとめてた。
[**2つ目**](https://qiita.com/AKpirion/items/be3d4e6c8ead22b64bee) とにかく全てが分からないから気の向くままに調べた内容をメモ。

[**mikanOS 3章を整理[前編]|後⇨**](https://qiita.com/AKpirion/items/d20a27bd57645a52df87)
# モヤモヤの原因は C 言語の理解不足
以下のサイトに出会い、世界が変わりました。

[**モノづくりC言語塾**](https://monozukuri-c.com/langc-recommended/)
ココでの説明も素晴らしいですが、併せて紹介されている
[**新・標準プログラマーズライブラリ C言語 ポインタ完全制覇**](https://www.amazon.co.jp/%E6%96%B0%E3%83%BB%E6%A8%99%E6%BA%96%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E3%82%BA%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA-C%E8%A8%80%E8%AA%9E-%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E5%AE%8C%E5%85%A8%E5%88%B6%E8%A6%87-%E5%89%8D%E6%A9%8B-%E5%92%8C%E5%BC%A5-ebook/dp/B077ZTQY62/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=XROALC1J16IM&keywords=C+%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E5%AE%8C%E5%85%A8%E5%88%B6%E8%A6%87&qid=1647083003&sprefix=c+%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E5%AE%8C%E5%85%A8%E5%88%B6%E8%A6%87%2Caps%2C143&sr=8-1)が
素晴らしかった(立ち読みだけど)

更に協力いただいた戦友も紹介します。

・[**Cの絵本 第2版 C言語が好きになる新しい9つの扉**](https://www.amazon.co.jp/C%E3%81%AE%E7%B5%B5%E6%9C%AC-%E7%AC%AC2%E7%89%88-C%E8%A8%80%E8%AA%9E%E3%81%8C%E5%A5%BD%E3%81%8D%E3%81%AB%E3%81%AA%E3%82%8B%E6%96%B0%E3%81%97%E3%81%849%E3%81%A4%E3%81%AE%E6%89%89-%E6%A0%AA%E5%BC%8F%E4%BC%9A%E7%A4%BE%E3%82%A2%E3%83%B3%E3%82%AF/dp/479815038X/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=25U0H3T5ZHUFW&keywords=C+%E3%81%AE%E7%B5%B5%E6%9C%AC&qid=1647082883&sprefix=c+%E3%81%AE%E7%B5%B5%E6%9C%AC%2Caps%2C157&sr=8-1)
  ほとんど絵での説明だったので分かりやすかった。
  導入としては個人的に 100点

・[**新・明解C言語 ポインタ完全攻略 (明解シリーズ)**](https://www.amazon.co.jp/%E6%96%B0%E3%83%BB%E6%98%8E%E8%A7%A3C%E8%A8%80%E8%AA%9E-%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E5%AE%8C%E5%85%A8%E6%94%BB%E7%95%A5-%E6%98%8E%E8%A7%A3%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E6%9F%B4%E7%94%B0-%E6%9C%9B%E6%B4%8B/dp/4797388536/ref=sr_1_4?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=XROALC1J16IM&keywords=C+%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E5%AE%8C%E5%85%A8%E5%88%B6%E8%A6%87&qid=1647083003&sprefix=c+%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E5%AE%8C%E5%85%A8%E5%88%B6%E8%A6%87%2Caps%2C143&sr=8-4)
  mikanOS のコードを読んでいて、一番謎だった "*" の存在。
  それを真っ先にクリアにしたかった。
  パラ読みして、イメージを作りながら自力で書き直すと自分の理解度が測れて楽しい。

もし C 言語の初見さんが mikanOS に挑戦している場合は
上記の入門書籍を眺める所から始めた方が賢明です。

# UEFI:構造体ベースのアクセスを理解
非難を覚悟で書きますが、UEFI で定義された IF の基本は**タンス**です。
例えば中身が空である 2段のタンスがあったとします。
1段目の引き出しに"リンゴ"と書いた紙を入れます。

数分後、タンスの2段目をあけてみると、あら不思議、
真っ赤で美味しそうなリンゴが出てくるではありませんか。

そんな事、ある??(笑)
そうです。実はタンスの裏側には、穴があって、
親切な誰かが願い事を書いた紙を見て、
こっそり2段目にリンゴをしまってくれただけなのです。

上記のような感じで OS ブートローダーとファーム(BIOS)は構造体(タンス)を介して
情報をやり取りしてシステムを運用しています。

では、具体的にどんな願い事をしているのでしょうか？
コードの流れは以下のイメージです(細かく言うとちょっと違うけど。。)。

![フローチャート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/ecbb8f01-5a20-b650-3aaf-75b2fc436aa5.png)

# メモリマップ作成 依頼
例えば Memory map を生成するために GetMemoryMap() なんてものが用意されています。
![getmemorymap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/e63f71f6-05fb-e864-6dfa-bebafef94220.png)
上図の補足にサラッと概要を書きましたが
要は、**メモリマップのファイルサイズだけを指定してください。**
**そのサイズでメモリマップを作りますよ。**
**ただし格納場所はwork memory に格納**ですよって話です。

アクセス手法の一例として構造体を使うアイディアがあります。
パラメータの集合を用意し、GetMemoryMap() に各パラメータを宛がえば OK です。

```GetMemoryMap_sample.c
//[注意]
//以下のコードで何してるか分からない人は、
//冒頭の参考書を読み直す事を推奨します.
//理解不足の原因は UEFI の仕様の理解不足ではなく、
//C 言語の理解、そのものが不足している事が原因である可能性が高いです。

struct MemoryMap {
  UINTN buffer_size;
  VOID* buffer;
  UINTN map_size;
  UINTN map_key;
  UINTN descriptor_size;
  UINT32 descriptor_version;
};

EFI_STATUS GetMemoryMap(struct MemoryMap* map) {
  map->map_size = map->buffer_size;
  return gBS->GetMemoryMap(
      &map->map_size,
      (EFI_MEMORY_DESCRIPTOR*)map->buffer,
      &map->map_key,
      &map->descriptor_size,
      &map->descriptor_version);
}

EFI_STATUS EFIAPI UefiMain(
    EFI_HANDLE image_handle,
    EFI_SYSTEM_TABLE* system_table) 
  {
    //status checker when access to API
    EFI_STATUS status;
    Print(L"Hello, AKpirios World!\n");

    //Make a memory map on workmemory
    CHAR8 memmap_buf[4096 * 4];
    struct MemoryMap memmap = {sizeof(memmap_buf), memmap_buf, 0, 0, 0, 0};
    GetMemoryMap(&memmap);

    while (1);
    return EFI_SUCCESS;
}
```

# Root Open 依頼
メモリマップは work memory に居るので
電源 OFF すると消えてしまう。

っであれば、ハードディスク(HD)にメモリマップを保存しましょう。
ハードディスクの領域はボリュームと言うらしいです。
UEFI には以下の仕様があります。

![openvolume.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/8d8578dc-7592-2721-0cd9-464930e39f65.png)

また EFI_SIMPLE_FILE_SYSTEM_PROTOCOL は OpenProtocol からアクセスが出来るようになる。
以下、証拠(すべてのプロトコル(タンス)は OpenProtocol を通して探し出すことが出来る)。
![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/55b50361-5b0c-f362-1dc1-85f4a3901899.png)

更に、EFI_SIMPLE_FILE_SYSTEM_PROTOCOL はファイルシステムをいじるので
デバイスハンドルなのかなっと考えてみる。

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/7f0eb7fd-e8dc-e1c6-381b-a0e7be37c957.png)


```Open_root_sample.c
// HD を open する所だけ切り抜いた
/*
[方針]
1.OpenProtocol で EFI_LOAD_IMAGE_PROTOCOL を Open
2.EFI_LOAD_IMAGE_PROTOCOL の DeviceHandle を Open
※DeviceHandle を Open する際は GUID で FileSystem を
　示す iD &gEfiSimpleFileSystemProtocolGuid を使う

*/
EFI_STATUS OpenRootDir(EFI_HANDLE image_handle, EFI_FILE_PROTOCOL** root) 
{
  EFI_STATUS status;
  EFI_LOADED_IMAGE_PROTOCOL* loaded_image;
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* fs;

  status = gBS->OpenProtocol(
      image_handle,
      &gEfiLoadedImageProtocolGuid,
      (VOID**)&loaded_image,
      image_handle,
      NULL,
      EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
  if (EFI_ERROR(status)) {
    return status;
  }

  status = gBS->OpenProtocol(
      loaded_image->DeviceHandle,
      &gEfiSimpleFileSystemProtocolGuid,
      (VOID**)&fs,
      image_handle,
      NULL,
      EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
  if (EFI_ERROR(status))
    return status;

return fs->OpenVolume(fs, root);

}

EFI_STATUS EFIAPI UefiMain(
    EFI_HANDLE image_handle,
    EFI_SYSTEM_TABLE* system_table) 
  {
    //open root dir by using file system
    EFI_FILE_PROTOCOL* root_dir;
    status = OpenRootDir(image_handle, &root_dir);
    if (EFI_ERROR(status)) {Print(L"Fail to open rootdir\n");}

    Print(L"All done\n");
    while (1);
    return EFI_SUCCESS;
```

# ファイル新規作成
HD を Open したので新規ファイルを作成してみます。
以下にある Open の力を借ります。
![open.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/956c675e-6902-5c0b-b95a-4d010c09c06b.png)
今さっき開いたばかりなのに、
何も開くものないでしょ？ っと思った方、正解です。
![open_補足.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/08db4a82-418c-b56b-a19f-b68c3a92c786.png)

```Open_sample.c
EFI_STATUS EFIAPI UefiMain(
    EFI_HANDLE image_handle,
    EFI_SYSTEM_TABLE* system_table) 
  {
    
    //make new file on root dir
    EFI_FILE_PROTOCOL* memmap_file;
    status = root_dir->Open(
         /*This*/       root_dir, 
         /*NewHandle*/  &memmap_file, 
         /*FileName*/   L"\\memmap",
         /*OpenMode*/   EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
    if (EFI_ERROR(status)) {Print(L"Fail to make file\n");}

    Print(L"All done\n");
    while (1);
    return EFI_SUCCESS;
}
```

# Memory Map ファイル保存
Workmemory に居る Memory Map を先ほど開いた新規ファイルに移します。
特に UEFI とか関係ないので取り合えずコピペで次に進みます(気が向いたら解説つけるかも)。


```saveMemMap_sample.c

const CHAR16* GetMemoryTypeUnicode(EFI_MEMORY_TYPE type) {
  switch (type) {
    case EfiReservedMemoryType: return L"EfiReservedMemoryType";
    case EfiLoaderCode: return L"EfiLoaderCode";
    case EfiLoaderData: return L"EfiLoaderData";
    case EfiBootServicesCode: return L"EfiBootServicesCode";
    case EfiBootServicesData: return L"EfiBootServicesData";
    case EfiRuntimeServicesCode: return L"EfiRuntimeServicesCode";
    case EfiRuntimeServicesData: return L"EfiRuntimeServicesData";
    case EfiConventionalMemory: return L"EfiConventionalMemory";
    case EfiUnusableMemory: return L"EfiUnusableMemory";
    case EfiACPIReclaimMemory: return L"EfiACPIReclaimMemory";
    case EfiACPIMemoryNVS: return L"EfiACPIMemoryNVS";
    case EfiMemoryMappedIO: return L"EfiMemoryMappedIO";
    case EfiMemoryMappedIOPortSpace: return L"EfiMemoryMappedIOPortSpace";
    case EfiPalCode: return L"EfiPalCode";
    case EfiPersistentMemory: return L"EfiPersistentMemory";
    case EfiMaxMemoryType: return L"EfiMaxMemoryType";
    default: return L"InvalidMemoryType";
  }
}

EFI_STATUS SaveMemoryMap(struct MemoryMap* map, EFI_FILE_PROTOCOL* file) {
  EFI_STATUS status;
  CHAR8 buf[256];
  UINTN len;

  CHAR8* header =
    "Index, Type, Type(name), PhysicalStart, NumberOfPages, Attribute\n";
  len = AsciiStrLen(header);
  status = file->Write(file, &len, header);
  if (EFI_ERROR(status)) {
    return status;
  }

  Print(L"map->buffer = %08lx, map->map_size = %08lx\n",
      map->buffer, map->map_size);

  EFI_PHYSICAL_ADDRESS iter;
  int i;
  for (iter = (EFI_PHYSICAL_ADDRESS)map->buffer, i = 0;
       iter < (EFI_PHYSICAL_ADDRESS)map->buffer + map->map_size;
       iter += map->descriptor_size, i++) {
    EFI_MEMORY_DESCRIPTOR* desc = (EFI_MEMORY_DESCRIPTOR*)iter;
    len = AsciiSPrint(
        buf, sizeof(buf),
        "%u, %x, %-ls, %08lx, %lx, %lx\n",
        i, desc->Type, GetMemoryTypeUnicode(desc->Type),
        desc->PhysicalStart, desc->NumberOfPages,
        desc->Attribute & 0xffffflu);
    status = file->Write(file, &len, buf);
    if (EFI_ERROR(status)) {
      return status;
    }
  }

  return EFI_SUCCESS;
}

EFI_STATUS EFIAPI UefiMain(
    EFI_HANDLE image_handle,
    EFI_SYSTEM_TABLE* system_table) 
  {
    //save memmap on new file
    status = SaveMemoryMap(&memmap,memmap_file);
    if (EFI_ERROR(status)) {Print(L"Fail to save memmap\n");}
    status = memmap_file->Close(memmap_file);
    if (EFI_ERROR(status)) {Print(L"Fail to close\n");}

    Print(L"All done\n");
    while (1);
    return EFI_SUCCESS;
}

```