2026/04/17

TangConsoleDCJ11MEMにおける2BSDの誤動作の調査 その3

誤動作の原因の二つ目が判明しました。

符号なし2進数と符号付2進数の比較

後述するSOFUB_MAPの実装に符号なし2進数と符号付2進数を比較しているコードがありました。直接の比較ではありませんでしたが、符号なし整数の値を符号付整数の変数に代入し、その変数と定数を比較していたため、意図した通りには動作しない場合がありました。

問題発生のシナリオは以下の通りです。

  1. メモリ不足のためプロセスのテキスト領域(サイズ>=32768)のswap領域への書き出し処理が始まる
  2. buf構造体のb_bcountにサイズが格納されてsofub_alloc()に渡される
  3. sofub_alloc()では符号付整数のcountにb_bcountが代入される
  4. 関数の中でcountを正の定数と比較する(if文)
  5. countは負の値となるため条件が不成立となる(条件が成立した場合はテキスト領域のコピー処理が実行される)
  6. 5.で条件が不成立となった結果テキスト領域がコピーされずに過去にコピーされた別のデータがswap領域に書き込まれる
  7. 後にテキスト領域がswap領域から読み込まれるが、無関係のデータが復元されるためプログラムが誤動作する

2.11BSDのCプログラムのスタートアップコードはpure executable用とseparate executable用の二種類あるのですが、誤動作していたプログラムテキストの先頭部分はpure用ではなくseparate用に置き換わっていました。先頭以外の部分にも他のプログラムのヒープ領域と思しきデータがありました。

 SOFUB_MAPとは

UNIBUS用のデバイスはDMAアドレスが18bitなので、メインメモリのアドレスが18bitを越える領域に直接アクセスすることができません。このため、PDP-11ではUNIBUS用のMMUが実装されているのですが、TangConsoleDCJ11MEMにはDMA用のMMUはありません。

そこで登場するのがSOFUB_MAPです。18bitのDMAアドレスでアクセスできる範囲に中間バッファを設け、このバッファで中継することでメインメモリ全領域とのI/Oを実現しています。ISAバスのバスマスタデバイスで1MBを越えるメインメモリを扱う際の技法であるbounce bufferと同類です。

SOFUB_MAPではDMAの前後に必要に応じてメモリコピーを実行するのですが、上述のシナリオでこのコピー処理がスキップされた結果、バッファに残っていた別のデータがディスクに書き込まれていました。 

通常のファイルI/Oでは転送サイズが32KB以上になることはないので正常に動作していましたが、swapデバイスとのI/Oでは32KB以上になることがあるため、問題が発生していました。

修正内容

問題の原因がわかってしまえば修正は簡単で、変数countの型を符号なしにしました。修正パッチ 

なお、DMA完了時に呼ばれるsofub_relse()ではcountはunsignedで宣言されているので修正の必要はありませんでした。

2026/04/10

TangConsoleDCJ11MEMにおける2BSDの誤動作の調査 その2

問題の原因の一つが判明しました。

DCJ11が駆動する場合のABORT信号の扱いが仕様を満たしていないのがメモリ内容の上書きを発生させていました。

ABORT信号の扱い

ABORT信号は双方向の信号で、CPUが駆動する場合と外部回路が駆動する場合があります。CPUが駆動する場合の扱いについては、DCJ11 UG 3-6に以下のように規定されています。 

 If an internally generated abort condition such as an MMU error or address error exists, the DCJ1l asserts ABORT during the first part of the cycle. If this type of abort occurs, the DAL, BS, and MAP information should be ignored for the remainder of the cycle.

 オリジナルのFPGAの実装ではCPUが駆動しているABORT信号については関知していないため、以下のシナリオでメモリが破壊されていました。

  1. haltコマンドのテキスト領域とスタック領域が物理アドレス上で隣接する場所に割り当てられる。アドレスは昇順にテキスト、スタックと並んでいる。
  2. スタックポインタが減算される。減算されたポインタの物理アドレスは上記のテキスト領域を指している。
  3. 2.で減算されたポインタへの書き込み命令が実行されようとする 。書き込むデータは#4で命令コードとして解釈するとIOTとなる。
  4. MMUによりスタック領域の範囲外へのアクセスが検出され、バスへの書き込みサイクルがABORT信号の駆動によって無効化されるが、バスサイクルそのものは発生する。
  5. FPGAはABORT信号の入力を関知せず、書き込みサイクルを実行する。
  6. 5.の書き込みによって隣接するテキスト領域の命令が上書きされてIOT命令になる
  7. MMUによってトラップが発生し、スタック領域に別のメモリ領域が割り当てられて拡張される。
  8. 4.で中断された書き込み命令が再度実行される。 
  9. haltコマンドが6.のIOT命令を実行して異常終了する。

 FPGAのメモリへの書き込み処理を変更して、ABORT信号がアサートされている場合にバスサイクルを無視するように修正したところ、2.11BSDのhalt命令の誤動作と、2.9BSDのカーネルビルド時の誤動作については再現しなくなりました。

TangConsoleDCJ11MEMにおける2BSDの誤動作の調査 その3

誤動作の原因の二つ目が判明しました。 符号なし2進数と符号付2進数の比較 後述するSOFUB_MAPの実装に符号なし2進数と符号付2進数を比較しているコードがありました。直接の比較ではありませんでしたが、符号なし整数の値を符号付整数の変数に代入し、その変数と定数を比較していたため...