天天看點

android qemu-kvm i8254 pit虛拟裝置

8259主片的IRQ0~7對應INT 8~INT F,從片的IRQ8~IRQ15對應INT 70~INT 77。

有份以前上C語言測控時寫的代碼,使用了8254的,輸入采樣周期(in ms)和采樣次數,每次采樣時列印一個'8'。

注意定時器的最大周期比較短,大約55ms,是以需要使用軟體方式擴大定時器的周期,注意周期不是10ms的倍數時的特殊處理。

定時器0工作于模式3,方波發生器。用學硬體的話來說,就是自動重裝定時器;用學軟體的話來說,就是周期定時器,不是oneshot的。

真的看完了,現在開始看模拟的。

8254的初始化是在pc_init1中執行的,設定iobase為0x40,IRQ為0,INT 8:

8254是有三個timer的,隻用到了channel 0的timer。

qemu有自己的定時器,輸入時鐘是1G,對應1ns。8254的輸入時鐘是1193kHZ,如何模拟的呢?

根據8254的設定,計算出來下一個中斷到臨的tick次數,在根據8254和qemu timer頻率的不同,對tick進行轉換,然後設定qemu timer的定時設定,當qemu timer逾時時,callback函數就是8254的中斷處理函數pit_irq_timer。在中斷函數中,再進行一些其它的處理,如重新裝載之類的。

qemu_register_reset是用連結清單儲存一些複位函數的:

當然pit_init最後也調用了pit_reset函數對寄存器進行複位,将mode設定為3,設定gate,計數值歸零:

這兩行設定了寄存器的讀寫函數,注意這裡是PMIO方式,不是MMIO方式的寄存器。0x40~0x43的寫函數設定為pit_ioport_write;0x40~0x42的讀函數設定為pit_ioport_read:

寫函數,看懂寄存器的使用後,這個函數還是比較簡單的:

pit_latch_count用于鎖存目前的計數值:

pit_load_count用于裝載計數值,count_load_time是裝載時tick的值(tick++ in every ns);count是8254的周期,8254自己的計數值會按照1193kHZ的頻率遞減的。注意和count_load_time機關的不同,以及後續機關的轉換。最後調用pit_irq_timer_update,對qemu timer進行更新。

pit_irq_timer_update函數幹兩件事:

1、計算irq_level,就是比較tick的值和設定的值,滿足條件時就會qemu_set_irq觸發中斷請求

2、計算expire_time,并且調用timer_mod更新qemu timer,讓qemu timer在8254下一個需要産生中斷的時候産生timeout,并調用callback,也就是8254的中斷函數

8254的中斷函數,也就是qemu timer的callback函數,也調用了pit_irq_timer_update:

寄存器的讀函數:

當kvm執行到PMIO的操作時,會退出,然後調用kvm_handle_io:

以8bit讀為例子:

PMIO的位址和opaque以及讀寫函數的綁定,使用register_ioport_read,register_ioport_write函數,在i8254.c的pit_init中調用的:

pit_save,pit_load,register_savevm用于快照和恢複的,可以不看。

現在qemu的8254都是使用了QOM模型了,這個模型太TMD的複雜了。另外hw/i386/kvm/timer/i8254.c中提供了kvm-pit,使用kvm提供的核心态的8254的模拟,中斷的處理和IO的讀寫都在核心态,不需要退出kvm了,速度要更快些。類似的,8259之類的也有kvm核心态的實作,是以說android emulator的性能還是有提升空間的。

繼續閱讀