MIPSのシステムレジスタ読み書きの機能を実装する
xv6のデバッグをしていて、何か動作がおかしいなと思っていたら、システムレジスタを定義していたものの、読み書きの動作を定義していなかった! そりゃ、うまくいかない。
とりあえず、システムレジスタの読み書きの仕組みだけ実装した。 例えば、割り込みが外部から発生したときは、このシステムレジスタを外から書き込むようにして、状態を保存させる。 まだ機能が不十分で、全てを実装できている訳ではないが。
CSRWrite (SYSREG_ADDR_EPC, GetPC()); Word_t cause; CSRRead (SYSREG_ADDR_CAUSE, &cause); cause &= ~(0x1f << 2); cause |= (CauseInt << 2); CSRWrite (SYSREG_ADDR_CAUSE, cause); CSRRead (SYSREG_ADDR_CAUSE, &cause); InfoPrint ("<Interrupt CAUSE = %08x>\n", cause);
とりあえず、xv6において割り込みが発生したときに、割り込み要因はこれで判定できるようになる。
trap(struct trapframe *tf) { uint exccode = tf->cause & CAUSE_EXC; // ← ここで実際に読まれているのは、C0のCAUSEレジスタの値 if(exccode == EXC_SYSCALL){ if(proc->killed) ...
ただし、これでもまだ十分ではないんだよなあ。どのIRQが発生したかについては、前述した 8259のモデルを作成しなければならない。
システムレジスタのテンプレート自動生成の仕組み
自分のISSでは、システムレジスタのテンプレートを作成すると、そこから自動的にC++のテンプレートを作成するような仕組みを作っている。
$sysreg_table[ 4] = Array[0x40, 'RW', 'Context', Array[Array[31,23,'PTEBase','RW'], Array[22, 4,'BadVPN2','R']]] $sysreg_table[ 5] = Array[0x41, 'RW', 'ContextConfig', Array[Array[31, 0,'VirtualIndex','RW']]] $sysreg_table[ 6] = Array[0x42, 'RW', 'UserLocal', Array[Array[31, 0,'UserInformation','RW']]] $sysreg_table[ 7] = Array[0x50, 'RW', 'PageMask', Array[Array[29,13,'Mask','RW'], Array[12,11,'MaskX','RW']]] $sysreg_table[ 8] = Array[0x51, 'RW', 'PageGrain', Array[Array[31,31,'RIE','RW'], Array[30,30,'XIE','RW'], Array[28,28,'ESP','RW'],
なんて風に書いておくと、
union { struct { uint32_t dummy_0 : 4; uint32_t BadVPN2 : 19; uint32_t PTEBase : 9; } bit_Context; uint32_t Context; } Context; struct { uint32_t VirtualIndex; } ContextConfig; struct { uint32_t UserInformation; } UserLocal;
こんな風に、各システムレジスタのビットをビットフィールドを使って生成してくれる。たとえ間違ってビットフィールドを作っていたとしても生成できるし、そもそもビット位置を間違えることが無くなる。 (というか今回はこのフィールドの生成を間違えていたハマっていたのだけれども...)