2013-05-16 128 views
1

我正在製作一個FPGA片上系統系統,在該系統中我使用內核模塊在運行時更改了硬件配置。他們的系統使用Linux 2.6和LEON3 CPU(SPARC)。有些位文件工作正常,但對於某些位文件,我收到「內核非法指令」或「無法在虛擬地址x處理內核分頁請求」。我很肯定我的硬件是正確的,因爲我已經用另一種方法測試了它,並且我改變的硬件與CPU無關。我猜這是一個軟件/內核模塊錯誤。這發生在我第14次進入內核模塊的寫入方法之後。我不知道從哪裏開始進行調試。任何幫助,將不勝感激。寫入內核模塊時發生內核非法指令

# cat x > /dev/y 
Unable to handle kernel paging request at virtual address 00001000 
tsk->{mm,active_mm}->context = 00000045 
tsk->{mm,active_mm}->pgd = fc013400 
       \|/ ____ \|/ 
       "@'/ ,. \`@" 
       /_| \__/ |_\ 
       \__U_/ 
cat(86): Oops [#1] 
PSR: f30000c7 PC: f0089e90 NPC: f0089e94 Y: 00000000 Not tainted 
PC: <vfs_write+0xb8/0x148> 
%G: 80080000 00001000 00000001 fd000100 00000dae f09c4370 fbca0000 0000fffb 
%O: 00001000 00000003 00001000 fe60e5dc fe60e430 fe60e420 fbca1e80 f0089e80 
RPC: <vfs_write+0xa8/0x148> 
%L: fbdcad40 00000000 fbca1e78 00000004 fbc0e940 00000000 fbdd2000 f0035784 
%I: 00001000 efe07b50 00001000 fbca1f40 00000000 00000000 000007af 000007af 
Disabling lock debugging due to kernel taint 
Caller[000007af]: 0x7af 
Instruction DUMP: d204200c f602600c c416e072 <84088001> 03000010 80a08001 02 
800018 b2102002 c404201c 
Killed 
# 

內核模塊寫入方法:

ssize_t icap_write(struct file *filp, char *buf, size_t count, loff_t *f_pos) { 
unsigned long words, data, *pdata, mem_loc_temp; 

pdata = (unsigned long *)buf; 
mem_loc_temp = icap_mem_loc;//reset mem_loc_temp 

while((pdata < (buf + count)) && (mem_loc_temp < icap_mem_loc+4096)){ 
     leon_store_reg(mem_loc_temp,*pdata); 
     pdata++; 
     mem_loc_temp+=4; 
    } 
} 

我覺得這是我的問題。 pdata正在走出界限。它是一個可行的修復到其修改爲: --------------更新的代碼-------------

unsigned long *pdata; 
static int __init icap_init(void) { 
... 
pdata = (unsigned long *)kmalloc(mem_size*sizeof(char), GFP_KERNEL); 
... 
} 

ssize_t icap_write(struct file *filp, const char *buf, 
        size_t count, loff_t *f_pos) { 

int i, cycles, spins; 
ssize_t result; 

if(count%4 != 0){ 
    printk(KERN_INFO "ERROR: count = %d is not a multiple of 4. count mod 4 = %u\n Assuming 0 padding for last word. Configuration may not have completed as expected.",count, count%4); 
    //return count; 
} 

result = copy_from_user(pdata, buf, count); 
if (result) { 
    printk(KERN_INFO "copy_from_user failed, returned: %d\n.", result); 
    return -EINVAL; 
} 
spins = 0; 
while((leon_load_reg(ctrl_mem_loc+8) & 0x10) == 0){//check done 
    spins++;//spin on NOT done 
} 
if (spins > 0) 
    printk(KERN_INFO "%d spins\n", spins); 

leon_store_reg(ctrl_mem_loc+8, 0);//deassert start 

if(count == 4096){ 
    cycles=min((unsigned long)1024, (unsigned long)mem_size/4); 
}else if((count > 0) && (count < 4096)){ 
    cycles=min(((unsigned long)count+3)/4, (unsigned long)mem_size/4); 
}else{ 
    printk(KERN_INFO "ERROR: count > 4096\n"); 
    cycles = 0; 
} 

for(i = 0; i < cycles; i++){ 
    leon_store_reg(icap_mem_loc+4*i, pdata[i]); 
} 

leon_store_reg(ctrl_mem_loc, cycles);//set number of samples 
leon_store_reg(ctrl_mem_loc+8, 0x1);//set start high 

return count; 
} 
+0

你確定你的修補程序有效嗎?因爲它看起來和我原來的一樣 - 寫-1條目,然後寫最後一條... –

+0

增加了更多的代碼來使它更清晰。我的內存空間是4096個字節,我希望buf有4096個字節,數量是4096.也許我現在應該對它進行硬編碼以避免混淆,因爲這不會被重用或任何其他內容。 – Stuart

+0

「4K」是一個內核頁面。你不是想用'copy_from_user'。 –

回答

1

你可能需要在這裏放置一些代碼,以便我們更好地幫助您。

我的第一個猜測是你在你的代碼中有一個數組覆蓋你的代碼,當你打到第14個條目時,它碰到系統需要的東西並導致異常。如果可能的話,在程序寫出來的時候跟蹤它,看它是否在寫它應該在的地方。不同的文件將在內存中的不同位置,如果這些區域不是系統關鍵的,它可能會解釋爲什麼它們不會崩潰,並且這是一個問題。從您的崩潰轉儲

一件有趣的事:

Unable to handle kernel paging request at virtual address 00001000

即4096十進制 - 一樣while循環偏移。所以也許有一些事情在那裏發生,但是你的代碼似乎並沒有解決它,並且可能會讓情況變得更糟,因爲在while循環中有兩個條件要注意。

更新爲你的代碼改變

鑄造pdataunsigned long *如果基指針不long對齊可能不是安全的。不知道你的系統是否允許未對齊的內存訪問,所以要小心。

icap_mem_loc是一個未知類型,但似乎是一個整數值?如果是這樣,那麼處理積分值和指針就是在尋求未來的麻煩。

您正在比較無符號長指針和while循環中的無符號字符指針 - 可能是安全的,但要小心指針算術。

您是否在編譯此代碼時啓用了警告?如果沒有,那麼這樣做,因爲我敢肯定它會大喊上述問題......

如果icap_mem_loc爲NULL或0,會發生什麼情況?如果這是一個錯誤條件,你需要處理它 - 你的頁面請求有點意味着這是一個糟糕的條件,你沒有任何ASSERT或條件來處理這個。

最後,mem_loc_temp+=4;4 - 你確定long4字節大小在您的系統上。應該是sizeof()'d或更好,而不是消除歧義。

不是試圖在這裏攻擊你,而是指出我看到的每一個潛在的失敗點,以便我們能夠爲你解決這個問題。

+0

我修改了這個問題。我想我發現了這個問題,但是會很感激反饋。 – Stuart

+0

以上評論 - 不要以爲是... –

+0

不用擔心,我真的很感謝幫助。我現在擺脫了大部分警告。我很確定系統不允許未對齊的訪問。 'icap_mem_loc'是一個int並且靜態設置。我通過將'buf + count'轉換爲'unsigned long'來修復'buf + count',在初始化方法中檢查大小。 – Stuart