ppcsim Ver0.96インターナル

目次

  1. このドキュメントについて
  2. 内部管理の概要
  3. メモリモデル
  4. スタックポインタとargc/argv
  5. システムコールエミュレーション
  6. IOデバイス
  7. その他
  8. 履歴

このドキュメントについて

当初は、すぐに使わなくなるだろうと思っていたppcsimですが、建て増しを続け ながら使い続けているうちに機能が増えてきたため、機能メモ/内部仕様書ということで 本ドキュメントを作成することにしました。興味のある方は読み物として読んでみて 下さい。

内部管理の概要

初期のppcsimは単純なCPU/メモリモデルのシミュレートしか行っていませんでしたが、 OS上で実行されるプロセスとして動作させた方が、プログラミングの勝手が良いで あろうということで、Ver0.50からプロセスの概念の導入とシステムコール エミュレーションを行うようになりました。

プロセスの管理といっても非常に単純なもので、プロセス毎にレジスタとメモリ を独立に持っているだけです。着目するプロセス(一番新しく生成された プロセス)を引き数としてシミュレーションコアエンジンに引き渡し、 シミュレーションを行います。

一つのプロセスが保持する情報

     ┌──────┐
     │ プロセス   │
     │----------- │
     │GPR,FPR,SPR │
     │Memory,etc  │
     └──────┘

実行イメージ

     ┌─────┐
     │プロセス0 │
     └─────┘                      ┌─────────┐
     │プロセス1 │                      │ シミュレーション │
     └─────┘ 実行を行うプロセスを │  コアエンジン    │
     │    :     │ シミュレーションコア └─────────┘
     └─────┘ で実行する		   ↑
     │プロセスn │────────────┘
     └─────┘


因みに、プロセス情報を独立に保持しているため、実行するプロセスを循環させれば、 マルチタスクっぽく動作する事も期待されますが、プロセス間通信や共有メモリ まで考慮に入れると、「クロス開発を行う為の」という目的がすりかわってしまう くらい面倒そうだったので、現在のような単一プロセス実行の方式になっています。

メモリモデル

小さなプログラムの実行を目的としていたため、多くても16MB程度しか使わないで あろうということで、メモリ空間は0x00000000から指定のメモリサイズ分だけ 確保するという事にしていました。ところが、何を血迷ったか、Linuxカーネルを ppcsim上で動作させようとしたとき、PCI空間のように非常に大きなアドレス空間 を使用する場合に不都合が生じました。32bitアドレスで表現できるメモリ空間 を割り当てられれば何の問題もありませんが、新世紀を迎えた世の中でも4GBの メモリをポンと割り当てることは ままなりませんので、苦肉の策としてセグメント 方式を導入することにしました。

ppcsim内では 32bitのアドレス空間を 16MB×256セグメント に分割しています。 各セグメントはそれぞれ独立にメモリサイズの割り当てが行えます。

 一つのプロセスが保持するセグメントリスト
    ┌───────┐
    │セグメント 0  │
    ├───────┤
    │セグメント 1  │
    ├───────┤
    │      :       │
    ├───────┤
    │セグメント 255│
    └───────┘

 一つのセグメントが保持する情報
    ┌─────────────┐
    │セグメント情報            │
    │------------------------- │
    │・セグメントのサイズ      │
    │・実メモリ領域へのポインタ│
    └─────────────┘


セグメントとPPC命令でアクセスするアドレス空間との対応は以下のようになっています。

               ┌──┬───────┐
  PPCアドレス  │0-7 │    8 - 31    │
               └──┴───────┘
                  │          ↓
                  │  ┌───────┐
                  │  │セグメント 0  │
                  │  ├───────┤
                  │  │セグメント 1  │
  シミュレータ    └→├───────┤
     メモリ           │      :       │
                      ├───────┤
                      │セグメント255 │
                      └───────┘


シミュレーションメモリの割り当ては memal コマンドで行います。セグメントの割り当て が無いか、セグメント内のセグメントサイズを越えるアクセスを行うとメモリアクセス例外が 発生します。

Ver0.96でのデフォルトメモリ割り当ては、以下のようになっています。

PPC> memal
---- Memory map of PID 0 ---
00000000 : size=00010000 , normal
01000000 : size=00800000 , normal
08000000 : size=01000000 , normal
80000000 : size=00010000 , shared
f0000000 : size=003c0000 , shared


Ver0.96では、0x80000000にはPseudo-IOデバイスポートが割り当たっています。 0xf0000000からはPseudo-グラフィックデバイス空間が割り当たっています。

各セグメントにはメモリ属性があり、normalとなっているものは、 fork()システムコール実行時に親プロセスからのコピーを引き継ぐ領域になります。 sharedはfork()システムコールを実行しても親プロセスのコピーではなく、 親プロセスと共通の領域として子プロセスに引き継がれます。 IOポートやGraphicRAMなどハード資源の割当たっているメモリ領域は sharedの属性を持っています。

スタックポインタとargc/argv

プログラムの実行を行う場合、引き数を与える場合があると思います。引き数 文字列の格納場所および、スタックポインタの先頭は以下のように セグメント0のメモリ領域の一番最後尾部分を使用しています。

      0x00000000┌───────────────┐
                │ユーザテキスト/データ領域     │
      0x00??????├───────────────┤
                │スタック領域                  │
     -0x00001000├───────────────┤
                │argvポインタリスト( 256byte)  │
     -0x00000f00├───────────────┤
                │argv文字列群      (3840byte)  │
      0x00XXXXXX└───────────────┘


SPはGPR1,argcはGPR3,argvはGPR4にそれぞれ格納されます。
引き数文字列が領域に収まらない場合はプロセスの起動に失敗します。 注意点として、スタックはアドレスの前方向に消費してゆきます。 そのため、スタックを大量に消費すると、データ領域を破壊する場合があります。 シミュレータではチェックできませんので、シミュレータ上で動作する プログラムでスタックオーバーフローを起こさないように配慮する必要があります。

システムコールエミュレーション

プロセスレベルでの実行を可能にするため、sc命令実行を解釈した時に 実際のハードウェアとは異なったOSレベルの動作を行います。
ppcsimではPPC-Linuxでのシステムコール実行方法を解釈するように実装されています。 実行の際に、GPR0にシステムコール番号、GPR3〜にシステムコールの引き数を 与えます。実際のラッピング方法はライブラリの実装に依存します。 Ver0.96では以下のシステムコールが実装されています。

No name 説明
1exit() シミュレータで検出したらプロセスを破棄する
2fork() シミュレータで検出したら子プロセス生成し実行を移す
3read() 同一関数をネイティブで実行
4write() 同一関数をネイティブで実行
5open() 同一関数をネイティブで実行
6close() 同一関数をネイティブで実行
10unlink() 何もしない
11execve() シミュレータで検出したら実行ファイルをロードする
12chdir() 同一関数をネイティブで実行
13time() 同一関数をネイティブで実行
15chmod() 同一関数をネイティブで実行
19lseek() 同一関数をネイティブで実行
20getpid() シミュレータ上で割り当てたプロセスIDを返します(for linux ELF)
24getuid() 500を固定で返す(for linux ELF)
30utime() 同一関数をネイティブで実行
33access() 同一関数をネイティブで実行
38rename() 同一関数をネイティブで実行
39mkdir() 同一関数をネイティブで実行
41dup() 同一関数をネイティブで実行
43times() 同一関数をネイティブで実行
45brk() ヒープ領域からシミュレータで領域を割り当てます(for linux ELF)
47getgid() 544を固定で返す(for linux ELF)
49geteuid()500を固定で返す(for linux ELF)
50getegid()544を固定で返す(for linux ELF)
54ioctl() TIOCGWINSZとTCGETSをネイティブで実行
55fcntl() 同一関数をネイティブで実行
60umask() 同一関数をネイティブで実行
63dup2() dup()をネイティブで実行
64getppid()シミュレータ上での親プロセスIDを返します
65getpgrp()getpgrp()をネイティブで実行
77getrusage()常に0を返す(for linux ELF)
78gettimeofday()同一関数をネイティブで実行
85readlink()同一関数をネイティブで実行
89readdir()同一関数をネイティブで実行
90mmap() シミュレータでヒープよりメモリを割り当てます。アドレスは 0のみ有効(for linux ELF)
91munmap() シミュレータでヒープに返す(for linux ELF)
106stat() 同一関数をネイティブで実行
107lstat() 同一関数をネイティブで実行
108fstat() 同一関数をネイティブで実行
114wait4() シミュレーターで自プロセスIDを返します
122uname() sysname :"ppcsim", nodename:"none", release :"92", version :"0", machine :"ppcsim" を返します
125_mprotect()0を固定で返します(for linux ELF)
133fchdir() 同一関数をネイティブで実行
140llseek() 同一関数をネイティブで実行(for linux ELF)
173rt_sigaction()0を固定で返します
174rt_sigprocmask()0を固定で返します
181chown() 同一関数をネイティブで実行
182getcwd() 同一関数をネイティブで実行
190ugetrlimit()0を固定で返します
195stat64() stat()をネイティブで実行しstat64()のフォーマットに変換(for linux ELF)
196lstat64()lstat()をネイティブで実行しlstat64()のフォーマットに変換(for linux ELF)
197fstat64()fstat()をネイティブで実行しfstat64()のフォーマットに変換(for linux ELF)
200isatty() PowerPCnewlib時はisatty()をネイティブで実行、 LinuxPPC時は未実装システムコール
202sbrk()|getdents64()PowerPCnewlib時はヒープよりメモリ割り当てを行い、 LinuxPPC時はgetdents64フォーマットに合わせてシミュレータ内で データを加工して返します
203pathconf()PowerPCnewlib時はpathconf()をネイティブで実行、 LinuxPPC時は未実装システムコール
204fcntl64()fcntl()をネイティブで実行
205_opendir()PowerPCnewlib時はフォーマットに合わせてシミュレータ内でデータを加工して 返します。LinuxPPC時は未実装システムコール
206closedir()PowerPCnewlib時はシミュレータ内でデータを処理し、 LinuxPPC時は未実装システムコール


同一関数をネイティブで実行するものは、引き数をそのままネイティブ関数 に渡すため、エンディアンや構造体メンバーの違いに注意する必要があります。

IOデバイス

システムコールエミュレーション実行を行う場合は、標準出力やファイルが 使用できるため、プログラムの実行結果などはそれらに出力すれば良いでしょう。 しかし、OSレベルの開発に使用する場合にはエミュレーションを使用することは できませんので、結果の出力や格納には本物のハードウェアと同様のデバイスを シミュレートする必要があります。

当初はIOデバイスを実装する予定は無かったのですが、Linuxカーネルを実行する という血迷った経緯を踏んだ事から、簡単なIOデバイスの実装を余儀されなく なりました。

Ver0.96で実装されているIOデバイスには以下のものがあります。

  1. 出力専用コンソールポート
  2. ディスクデバイス
  3. 時計デバイス
  4. グラフィックデバイス
  5. FDCデバイス(不完全)
  6. 割り込みコントローラ(不完全)
  7. シリアルデバイス(不完全)

実際にアクセスするには、それぞれのアクセスポートの割り当てられたアドレス空間を、 手順に従い操作することで行います。
----出力専用コンソールポート------------------------
ポートアドレス

#define CONSOLE_OUT_CMD      0x8000ff00

CONSOLE_OUT_CMDにint型で0以外の値を書きこむとppcsimコンソールに一文字出力 されます。

----ディスクデバイス--------------------------------
ポートアドレス

DISK_CMD             0x8000ff10
DISK_ACK             0x8000ff14
DISK_DRIVE           0x8000ff18
DISK_TRACK           0x8000ff1c
DISK_DATA_TOP        0x8000ff20
DISK_DATA_LEN        0x8000ff24

DISK_DRIVEにドライブ番号、DISK_TRACKにアクセスするトラック番号、 DISK_DATA_TOPにディスクからの読みこみもしくは書き込みを行う メモリアドレス、DISK_DATA_LENに読みこみもしくは書き込みを行う バイト数を指定し、最後にDISK_CMDにアクセスコマンドを書き込みます。 アクセスコマンドには以下の種類があります。
DISK_INQ             0x01
DISK_READ            0x02
DISK_WRITE           0x03
DISK_WRITE_INQ       0x04

----時計デバイス------------------------------------
ポートアドレス

CLOCK_READ_CMD        0x8000ff30
CLOCK_DATA            0x8000ff34

CLOCK_READ_CMDにint型で0以外の値を書きこむと、CLOCK_DATAに4byteの timi_t型の現在時刻がセットされます。

----グラフィックデバイス----------------------------
ポートアドレス

GRAPHIC_VRAM_SYNC     0x8000ff40
GRAPHIC_VRAM_TOP      0xf0000000 〜

グラフィックデバイスは GRAPHIC_VRAM_TOPから、2048×480×32bit の実アドレス空間に 割り当てられています。実際の表示はGRAPHIC_VRAM_TOPから640×480のサイズと なっています。画素は Alpha=8bit,Red=8bit,Green=8bit,Blue=8bitの32bitで 表されます。内部で描画バッファリングを行っているため、直ぐには描画に反映されません。 バッファをフラッシュするには0x8000ff40にint型で1を書き込む事でバッファを フラッシュし、アクセスしたデータが表示に反映されます。

----FDCデバイス-------------------------------------
ポートアドレス

FDC_PORT_TOP   0x800003f0
FDC_PORT_BOT   0x800003f7

未稿

----割り込みコントローラ----------------------------
ポートアドレス

PIC_MASTER_PORT0     0x80000020
PIC_MASTER_PORT1     0x80000021
PIC_SLAVE_PORT0      0x800000a0
PIC_SLAVE_PORT1      0x800000a1

未稿

----シリアルデバイス--------------------------------
ポートアドレス

SERIAL_DATA_PORT0      0x800003f8
SERIAL_SCR_PORT0       0x800003ff

未稿

これらのデバイスを使用し、PPCネイティブ動作するOSが開発されれば ppcsim自体を他のOSに移植した場合でも、全く同じ様にシミュレーション動作 させる事が可能なハズですが、このドキュメントを書いている時点では 実物のPPCネイティブ動作可能なppcsimで動作するOSは存在していません(^^;

その他

現在の実装状態は以上のようになっています。この先、都合により仕様が変更されたり 追加、削除される項目なども出てくると思うのですが、そういうものだという事で ご了承いただきたいと思いますm(_'_)m。
また、説明不足な点や間違いについては、随時加筆修正していく予定です。

履歴

  2001/09/30 : 0.70ベース初版
  2001/10/28 : 細かい言いまわしを修正。
  2001/11/25 : 0.73ベースで書き換え。スタックポインタとargc,argvの項目を追加。
  2004/09/18 : 0.96ベースで書き換え。


TOP PREV