シリアル入力のIRQのデバッグ
作って理解する仮想化技術を読むで、自分で書いている方だけシリアル入力(UARTデバイスからの入力)が1文字で止まってしまうバグを修正する
デッドロックの可能性を考えたので、まずvm::input_uart();にデバイスを引数として渡すシグニチャを変えて、関数の短いスコープでロックが行われるようにした
これによってデバッグ出力の際にデッドロックすることも無くなった
次にexception::irq_handler();でどこまで呼び出されているかデバッグ出力をして調べた
code:rs
let pl011_int_id = (*PL011_DEVICE.lock()).interrupt_number;
if interrupt_number == pl011_int_id {
log_debug!("ok1");
vm::input_uart();
log_debug!("ok2");
}
最初は両方出るがその次の割り込みからこのデバッグ出力も出なくなるのでGICの設定か復帰するときの処理に問題がある可能性を考えた
そもそもpl011_int_idと一致する割り込み番号からなる割り込みがその後のシリアル入力から発生していない
GICのDistributorとRedistributorの初期化処理と、irq_handler()の後半の処理は間違えていなそう
これを間違えてた
UARTのMMIOで用意していないレジスタが1つあり、UART_ICRレジスタは割り込みの停止をするレジスタだったため実装した
ARM PrimeCell UART (PL011) Technical Reference Manual
code:rs
UART_ICR => {
self.raw_interrupt_status &= !(value as u16);
}
IRQの終わりに行っている処理は2つあり、CPUの割り込み優先度の復帰と終了を別々に行っている
code:rs
GicRedistributor::drop_priority(interrupt_number, group);
if deactivate {
// CPUに割り込みの終了を通知
GicRedistributor::deactivate(interrupt_number);
}
このように処理を分けて行うにはICC_CTLR_EL1レジスタのICC_CTLR1_EL1_EOI_MODEのビットを1にする
ICC_CTLR1_EL1_EOI_MODEは1になっていたので問題ない
UART_ICRへの書き込みは以下のログのように発生している
code:_
DEBUG write UART_ICR write: 2000
DEBUG write UART_ICR write: 80
EXT4-fs (vda): orphan cleanup on readonly fs
EXT4-fs (vda): mounted filesystem c3f839c4-25c5-473e-9689-3b06e6611d95 ro with ordered data mode. Quota mode: disabled.
VFS: Mounted root (ext4 filesystem) readonly on device 254:0.
devtmpfs: mounted
Freeing unused kernel memory: 1152K
Run /sbin/init as init process
EXT4-fs (vda): re-mounted c3f839c4-25c5-473e-9689-3b06e6611d95 r/w. Quota mode: disabled.
Saving 256 bits of non-creditable seed for next boot
Starting syslogd: OK
Starting klogd: OK
Running sysctl: OK
Starting network: Waiting for interface eth0 to appear............... timeout!
run-parts: /etc/network/if-pre-up.d/wait_iface: exit status 1
FAIL
Starting crond: OK
Welcome to Buildroot
buildroot login: DEBUG irq_handler ok1
DEBUG irq_handler ok2
DEBUG write UART_ICR write: 0
急に解決した
シリアル入力のIRQのデバッグ#6a07d96b0000000000f4a50bのdeactivateのフラグがデフォルトでfalseになっていて、割り込み通知を終了するGicRedistributor::deactivate(interrupt_number);が呼ばれていなかった
UART_ICRに0が書き込まれる挙動について
Claude.icon RX/RT/TXはFIFO読み書きによって自然にクリアされるため、ICRで明示的にクリアしません。Linuxドライバはこの仕様を理解した上で、「ICRには自動クリアされない割り込みだけをクリアする」という設計になっています