Why can't I register edge triggered interrupts in Linux 2.6.26? - linux

First time poster, so please excuse any stupidity.
I'm working on porting a custom CPLD driver on a linux kernel built for an MPC83xx from 2.6.22 to 2.6.26, and am receiving an unexpected Oops. The driver works fine for the .22 kernel, but the .26 kernel chokes on my call to request_irq. Does anybody know why there was a change in behavior, or better yet, what I need to do to resolve it?
I've tracked down the source of the Oops to a call to kernel/irq/manage.c, where desc->chip->enable(irq) is called in setup_irq(), and it looks like the function pointer to enable is being cleared in a call to ipic_set_irq_type() in arch/powerpc/sysdev/ipic.c. Unfortunately, I have no clue why.
I've included both the Oops and a sample kernel modules that replicates the issue.
Oops -
Unable to handle kernel paging request for instruction fetch
Faulting instruction address: 0x00000000
Oops: Kernel access of bad area, sig: 11 [#1]
PREEMPT SCPA-G2
Modules linked in: cpld(+)
NIP: 00000000 LR: c004b930 CTR: 00000000
REGS: df8b5df0 TRAP: 0400 Not tainted (2.6.26-twacs-100.0.0)
MSR: 20001032 <ME,IR,DR> CR: 24022422 XER: 20000000
TASK = dfbcfc00[488] 'insmod' THREAD: df8b4000
GPR00: 00000000 df8b5ea0 dfbcfc00 00000017 00000001 00000001 00000000 c02d1fb4
GPR08: 00002268 00000000 00000000 00000000 44022484 10073f68 1ffcb000 007ffeb0
GPR16: 00000000 00000000 00800000 00000000 bffff7f0 00000000 1006e3dc 00000000
GPR24: 00000002 00000000 00000000 00009032 df9d04c0 00000017 df8b4000 c02d40e4
NIP [00000000] 0x0
LR [c004b930] setup_irq+0x404/0x430
Call Trace:
[df8b5ea0] [c004b8ec] setup_irq+0x3c0/0x430 (unreliable)
[df8b5ed0] [c004bbd8] request_irq+0xe0/0x130
[df8b5f00] [e1078054] cpld_init+0x54/0xd0 [cpld]
[df8b5f10] [c0048ba0] sys_init_module+0x14c/0x1d8
[df8b5f40] [c0010008] ret_from_syscall+0x0/0x38
--- Exception: c01 at 0xff27bb0
LR = 0x10019ca8
Instruction dump:
XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
Kernel panic - not syncing: Fatal exception
Module -
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
static unsigned int cpld_virq = NO_IRQ;
unsigned value = 0xdeadbeef;
static irqreturn_t cpld_isr(int irq, void *dev_id) {
return IRQ_HANDLED;
}
void __exit cpld_cleanup(void) {
free_irq(cpld_interrupt, &value);
irq_dispose_mapping(cpld_virq);
return;
}
int __init cpld_init(void) {
int retval;
unsigned long cpld_interrupt = 23;
cpld_virq = irq_create_mapping(NULL, cpld_interrupt);
if (cpld_virq == NO_IRQ) {
return -EBUSY;
}
retval = request_irq(cpld_virq, cpld_isr,
IRQF_DISABLED | IRQF_SHARED | IRQF_TRIGGER_FALLING,
"CPLD", &value);
if (retval) {
irq_dispose_mapping(cpld_virq);
return retval;
}
return 0;
}
module_init(cpld_init);
module_exit(cpld_cleanup);
MODULE_LICENSE("Dual BSD/GPL");
Thanks for the help. I've been beating my head on this for several days now, and am open to any suggestions.

My copy of the 2.6.26 kernel's ipic_set_irq_type() doesn't do anything with the enable() pointer. It does, however, have the following comments that were not in 2.6.22:
/* ipic only supports low assertion and high-to-low change senses
*/
and
/* ipic supports only edge mode on external interrupts */
It looks like what you were doing on 2.6.22 was not supported by the hardware.

Aaah. Sounds like JayM was close, though not for the reason he thought. I just saw a patch go across the linuxppc-dev mailing list stating that edge supported interrupts were broken.
http://lkml.org/lkml/2010/5/3/363
This may not be the root cause, but is indicative of a problem I'm not going to fix.

Related

u-boot hard fault error after ram initialization

I have ported U-boot on my Waveshare coreH7 stm32h743 board. I have used stm32h743-disco files and device trees as a template for porting. my onboard SDRAM is IS42S16400J that is 8MBytes. I have calculate the parameters of my sdram and I put them in my board device tree file as shown as below:
/*
* Memory configuration from sdram datasheet IS42S32800G-6BLI
* firsct bank is bank#0
* second bank is bank#1
*/
bank2: bank#1 {
st,sdram-control = /bits/ 8 <NO_COL_8
NO_ROW_12
MWIDTH_16
BANKS_4
CAS_3
SDCLK_2
RD_BURST_EN
RD_PIPE_DL_0>;
st,sdram-timing = /bits/ 8 <TMRD_1
TXSR_1
TRAS_1
TRC_6
TRP_2
TWR_1
TRCD_1>;
st,sdram-refcount = <300>;
};
also, I have been configured the rcc values to feed the dram with 100MHz.
but when uboot starts initialization, it goes to hard fault interrupt.
this is the log:
lib/fdtdec.c:fdtdec_setup_mem_size_base_fdt() fdtdec_setup_mem_size_base_fdt: Initial DRAM size 2000000
include/initcall.h:initcall_run_list() initcall: 08008a89
common/board_f.c:setup_dest_addr() Monitor len: 00039F80
common/board_f.c:setup_dest_addr() Ram size: 02000000
common/board_f.c:setup_dest_addr() Ram top: D2000000
include/initcall.h:initcall_run_list() initcall: 08008665
include/initcall.h:initcall_run_list() initcall: 0800117d
arch/arm/lib/cache.c:arm_reserve_mmu() TLB table from d1ff0000 to d1ff4000
include/initcall.h:initcall_run_list() initcall: 080088c3
include/initcall.h:initcall_run_list() initcall: 080088c7
include/initcall.h:initcall_run_list() initcall: 080086b1
common/board_f.c:reserve_uboot() Reserving 231k for U-Boot at: d1fb6000
include/initcall.h:initcall_run_list() initcall: 080088ed
common/board_f.c:reserve_malloc() Reserving 1032k for malloc() at: d1eb4000
include/initcall.h:initcall_run_list() initcall: 08008821
Hard fault
pc : 0800087e lr : 00000000 xPSR : 21000000
r12 : d1eb3ff0 r3 : 00000000 r2 : 00000010
r1 : 00000000 r0 : d1eb3fb0
Resetting CPU ...
what is the problem? is ram initialized unsuccessfully? why? maybe wrong parameters? how Can I know that ram is initialized successfully?
this is normal info log of u-boot:
U-Boot 2020.07-00610-g610e1487c8-dirty (Aug 04 2020 - 00:34:13 +0430)
Model: Waveshare STM32H743i-Coreh7 board
DRAM: Hard fault
pc : 0800087e lr : 00000000 xPSR : 21000000
r12 : d1eb3ff0 r3 : 00000000 r2 : 00000010
r1 : 00000000 r0 : d1eb3fb0
Resetting CPU ...
your ram size is wrong. as you mentioned, the actual ram size is 8MB that is 0x7A1200 in hexadecimal number. but in U-boot log is "Initial DRAM size 2000000". you should change it in the device tree of your board.
memory {
device_type = "memory";
reg = <0xd0000000 0x7A1200>;
};

How to control the order of sections in MSVC x86 compiled binaries?

For some very low level application I require to have the entry point as the lowest/first item in the binary.
To do this I've done the following:
Placed the entry point in "function_ordering.txt" and passed it to the /ORDER linker option.
Generated a map file to see the following sections exist:
Preferred load address is 00040000
Start Length Name Class
0001:00000000 000210fdH .text$di CODE
0001:00021100 002d7b0cH .text$mn CODE
0001:002f8c10 00027874H .text$x CODE
0001:00320490 0000830fH .text$yd CODE
0002:00000000 000005d8H .idata$5 DATA
0002:000005d8 00000004H .CRT$XCA DATA
0002:000005dc 00000004H .CRT$XCAA DATA
0002:000005e0 00000004H .CRT$XCL DATA
0002:000005e4 00001554H .CRT$XCU DATA
0002:00001b38 00000004H .CRT$XCZ DATA
0002:00001b3c 00000004H .CRT$XIA DATA
0002:00001b40 00000004H .CRT$XIAA DATA
0002:00001b44 00000004H .CRT$XIC DATA
0002:00001b48 00000004H .CRT$XIY DATA
0002:00001b4c 00000004H .CRT$XIZ DATA
0002:00001b50 00057fb8H .rdata DATA
And that WinMain was AFTER this data. So I figured I need to put the function in its own section to make it "first".
So I put it in its own section:
#pragma code_seg("test")
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
return 0;
}
But this put the function even further away! This is because my test section appeared after the other sections.
So then I did this:
#pragma code_seg(".text$di")
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
return 0;
}
Which resulted in:
Address Publics by Value Rva+Base Lib:Object
0000:00000000 ___guard_fids_count 00000000 <absolute>
0000:00000000 ___guard_flags 00000000 <absolute>
0000:00000000 ___guard_fids_table 00000000 <absolute>
0000:00000000 __except_list 00000000 <absolute>
0000:00000ec7 ___safe_se_handler_count 00000ec7 <absolute>
0000:00009876 __ldused 00009876 <absolute>
0000:00009876 __fltused 00009876 <absolute>
0000:00000000 ___ImageBase 00040000 <linker-defined>
0001:00000000 _WinMain#16 00041000 f lol.obj
BOOM it worked. But this seems very much like a nasty hack.
So the question is.. is there a "supported" or "documented" way to get my section to appear first?

C++ read wav file, subchunk1size = 65536?

I wrote a program that read information from a .wav file . I received the following results.
chunkId :RIFF
chunkSize :7828798
format :WAVE
Junk chunk :JUNK
Junk size :92
Format chunk :fmt
format chunk size :65536
Audioformat :1
numberofchanels :48000
samplerate :1996488704
Byterate :131073
BlockAlign :16
bitspersample :0
subchunk2Id :
subchunk2Size :0
I don't know why wav header like that. I use sox program to look header of this wav file .
And here is result
Input File : 'AUDIO00001.wav'
Channels : 1
Sample Rate : 48000
Precision : 16-bit
Duration : 00:01:17.28 = 3709621 samples ~ 5796.28 CDDA sectors
File Size : 7.83M
Bit Rate : 810k
Sample Encoding: 16-bit Signed Integer PCM
Why's my program wrong??
I update my question:
my wav header structure:
struct WAVHEADER
{
char chunkID[4];
unsigned int chunkSize;
char format[4];
char junkChunk[4];
int junkSize;
char junkData[92];
char bext[4];
unsigned int bextSize;
char bextData[602];
char subchunk1ID[4];
unsigned int subchunk1Size;
unsigned short audioFormat;
unsigned short numberOfChanels;
unsigned int sampleRate;
unsigned int byteRate;
unsigned short blockAlign;
unsigned short bitsPerSample;
char subchunk2ID[4];
unsigned int subchunk2Size;
};
wav class:
wav::wav(const char* filepath)
{
std::ifstream wavFile(filepath);
if(wavFile.is_open())
{
wavFile.read((char*)&wavHeader,sizeof(WAVHEADER));
}
}
My confusing is that when subChunk1ID is correct ("fmt") , why does subchunk1Size seem wrong ??
The compiler seems to insert 2 bytes of padding between subchunk1ID and subchunk1Size. This is my hypothesis because
what you are seeing is that the struct is shifted 2 bytes "to the left"
and when I tested this I noticed that sizeof(WAVHEADER) was 2 bytes larger than it should be.
I'm not a C++ compiler expert so I can't tell you why it does that except that certainly it's allowed to do so.
To show visually, what the struct should look like at that point is this:
...
subchunk1ID:
01100110
01101101
01110100
00100000
subchunk1Size:
00101000
00000000
00000000
00000000
audioFormat:
00000001
00000000
numberOfChanels:
00000001
00000000
...
But instead what you are getting is this:
...
subchunk1ID:
01100110
01101101
01110100
00100000
2 bytes of pad:
00101000
00000000
subchunk1Size:
00000000
00000000
00000001
00000000
audioFormat:
00000001
00000000
...
00000000 00000000 00000001 00000000 is the number 65536 in little endian byte order.
So really what you have found is that this struct casting is not a reliable way to read a file. You should read it "the hard way" instead (one number at a time). There are other pitfalls like this one such as:
Endianness.
Existence, size and ordering of particular subchunks is not consistent in a WAV file. There is no reason JUNK and bext need to appear in the file, nor do they need to be sizes known at compile time of the program.

Why can't I handle NMI?

I want to handle NMI and do something when NMI occur. Firstly I write a naive nmi handler:
static irqreturn_t nmi_handler(int irq, void* dev_id) {
printk("-#_#- I'm TT, I am handling NMI.\n");
return IRQ_HANDLED;
}
And write a module to register my nmi handler, then use APIC to trigger NMI 5 times:
static void __init ipi_init(void) {
printk("-#_#- I'm coming again, hahaha!\n");
int result = request_irq(NMI_VECTOR,
nmi_handler, IRQF_DISABLED, "NMI Watchdog", NULL);
printk("--- the result of request_irq is: %d\n", result);
int i;
for (i = 0; i < 5; ++i) {
apic->send_IPI_allbutself(NMI_VECTOR);
ssleep(1);
}
}
Now I type "insmod xxx.ko" to install this module, after that, I check the /var/log/syslog:
kernel: [ 1166.231005] -#_#- I'm coming again, hahaha!
kernel: [ 1166.231028] --- the result of request_irq is: 0
kernel: [ 1166.231050] Uhhuh. NMI received for unknown reason 00 on CPU 1.
kernel: [ 1166.231055] Do you have a strange power saving mode enabled?
kernel: [ 1166.231058] Dazed and confused, but trying to continue
kernel: [ 1167.196293] Uhhuh. NMI received for unknown reason 00 on CPU 1.
kernel: [ 1167.196293] Do you have a strange power saving mode enabled?
kernel: [ 1167.196293] Dazed and confused, but trying to continue
kernel: [ 1168.201288] Uhhuh. NMI received for unknown reason 00 on CPU 1.
kernel: [ 1168.201288] Do you have a strange power saving mode enabled?
kernel: [ 1168.201288] Dazed and confused, but trying to continue
kernel: [ 1169.235553] Uhhuh. NMI received for unknown reason 00 on CPU 1.
kernel: [ 1169.235553] Do you have a strange power saving mode enabled?
kernel: [ 1169.235553] Dazed and confused, but trying to continue
kernel: [ 1170.236343] Uhhuh. NMI received for unknown reason 00 on CPU 1.
kernel: [ 1170.236343] Do you have a strange power saving mode enabled?
kernel: [ 1170.236343] Dazed and confused, but trying to continue
It shows that I register nmi_handler successfully(result=0), and NMI were triggered 5 times, but I didn't find sting that should be outputed in nmi_handler.
I work on Ubuntu 10.04 LTS, Intel Pentium 4 Dual-core.
Does it mean my NMI handler didn't execute?
How do I handler NMI in Linux?
Nobody?
My partner gave me 3 more days, so I read the source code and ULK3, now I can answer question 1:
Does it mean my NMI handle didn't execute?
In fact, IRQ number and INT vector number are different! The function request_irq() call setup_irq():
/**
* setup_irq - setup an interrupt
* #irq: Interrupt line to setup
* #act: irqaction for the interrupt
*
* Used to statically setup interrupts in the early boot process.
*/
int setup_irq(unsigned int irq, struct irqaction *act)
{
struct irq_desc *desc = irq_to_desc(irq);
return __setup_irq(irq, desc, act);
}
Look at this: #irq: Interrupt line to setup
. The argument irq is interrupt line number, not interrupt vector number. Look up ULK3 PDF, P203, Timer interrupt has IRQ 0, but its INT nr is 32! So I trigger the INT2(NMI) but my handler handle the INT34 actually! I want to find more evidence in source code(e.g. how to convert IRQ to INT? I modify my handler and init, I request irq=2, and Linux allot INT=50), but get nothing, expect linux-xxx/arch/x86/include/asm/irq_vectors.h
/*
* IDT vectors usable for external interrupt sources start
* at 0x20:
*/
#define FIRST_EXTERNAL_VECTOR 0x20
Wait me for a while...let me read more codes to answer question 2.

Accessing external bus in kernel space on an ARM based board

I'm trying to write an LCD display driver on an ARM based board.
The LCD controller is plugged on the external memory bus.
So I try to convert the physical address of registers of the controller to the virtual one.
I use the following pieces of code to do that :
#define AT91_VA_BASE_CS2 phys_to_virt(0x50000000)
static inline unsigned char at91_CS2_read(unsigned int reg)
{
void __iomem *CS2_base = (void __iomem *)AT91_VA_BASE_CS2;
return __raw_readb(CS2_base + reg);
}
static inline void at91_CS2_write(unsigned int reg, unsigned char value)
{
void __iomem *CS2_base = (void __iomem *)AT91_VA_BASE_CS2;
__raw_writeb(value, CS2_base + reg);
}
void write_lcd_port (int mode, unsigned char cmd_dat)
{
while ((read_lcd_port() & 0x03) != 0x03) {
/* wait while LCD is busy!!! */
} /* endwhile */
/* Send Command */
if (mode == 1)
{
at91_CS2_write(4, cmd_dat);
}
/* Send Data */
if (mode == 0)
{
at91_CS2_write(0, cmd_dat);
}
}
I get the following message :
Unable to handle kernel paging request at virtual address 4f000004
pgd = c39bc000
[4f000004] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: module_complet dm9000 at91_wdt vfat fat jffs2 nls_iso8859_1 nls_cp437 nls_base usb_storage sd_mod sg scsie
CPU: 0
PC is at read_lcd_port+0x1c/0x38 [module_complet]
LR is at 0x1
pc : [<bf0a21b8>] lr : [<00000001>] Tainted: P
sp : c380bf1c ip : 60000093 fp : c380bf2c
r10: 0003a804 r9 : c380a000 r8 : c001de64
r7 : 00000000 r6 : fefff000 r5 : 0000009c r4 : 00000001
r3 : 4f000000 r2 : 00000000 r1 : 00001438 r0 : bf0a25cc
Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment user
Control: C000717F Table: 239BC000 DAC: 00000015
Process insmod (pid: 903, stack limit = 0xc380a198)
Stack: (0xc380bf1c to 0xc380c000)
bf00: 00000001
bf20: c380bf44 c380bf30 bf0a21f4 bf0a21ac 00000000 fefa0000 c380bf54 c380bf48
bf40: bf0a2288 bf0a21e4 c380bf64 c380bf58 bf0a246c bf0a2280 c380bf84 c380bf68
bf60: bf0a4058 bf0a2464 40017000 c01c89a0 bf0a2d80 c01c8990 c380bfa4 c380bf88
bf80: c004cd20 bf0a4010 00000003 00000000 0000000c 00000080 00000000 c380bfa8
bfa0: c001dcc0 c004cbc8 00000000 0000000c 00900080 40017000 0000162e 00041050
bfc0: 00000003 00000000 0000000c bea0fde4 bea0fec4 00000002 0003a804 00000000
bfe0: bea0fd10 bea0fd04 0001b290 400d1d20 60000010 00900080 20002031 20002431
Backtrace:
[<bf0a219c>] (read_lcd_port+0x0/0x38 [module_complet]) from [<bf0a21f4>] (write_lcd_port+0x20/0x80 [module_complet])
r4 = 00000001
[<bf0a21d4>] (write_lcd_port+0x0/0x80 [module_complet]) from [<bf0a2288>] (wr_cmd+0x18/0x1c [module_complet])
r5 = FEFA0000 r4 = 00000000
[<bf0a2270>] (wr_cmd+0x0/0x1c [module_complet]) from [<bf0a246c>] (lcd_init+0x18/0x80 [module_complet])
[<bf0a2454>] (lcd_init+0x0/0x80 [module_complet]) from [<bf0a4058>] (mon_module_init+0x58/0xcc [module_complet])
[<bf0a4000>] (mon_module_init+0x0/0xcc [module_complet]) from [<c004cd20>] (sys_init_module+0x168/0x2c8)
r6 = C01C8990 r5 = BF0A2D80 r4 = C01C89A0
[<c004cbb8>] (sys_init_module+0x0/0x2c8) from [<c001dcc0>] (ret_fast_syscall+0x0/0x2c)
r7 = 00000080 r6 = 0000000C r5 = 00000000 r4 = 00000003
Code: e59f001c eb3e43c2 e3a0344f e59f0014 (e5d34004)
Segmentation fault
Note that this method works for internal peripherals (such as timers).
So in some cases, phys_to_virt works.
I think that no page is allocated at the address 0x50000000. How can I allocate a page at this specific address ?
I found functions like kmap but it seems to be very complicated and I don't know how to use it.
The best way to access memory-mapped peripherals is with the kernel's ioremap and friends.
First, declare that you want to use a specific region of memory for your peripheral:
struct resource *res = request_mem_region(0x50000000, region_size, "at91");
When you unload your driver, you will want to free that memory region.
release_mem_region(0x50000000, region_size);
Now, you can remap the I/O region before use.
void *ptr = ioremap(0x50000000, region_size);
If you want to prevent caching of these registers, use ioremap_nocache instead. You can also only remap a subregion of your device's memory space if you're only using that part.
Now that you have the iomapped region, you can do I/O on that memory.
iowrite8(value, (char *)ptr + reg);
unsigned int val = ioread8((char *)ptr + reg);
Once you're done reading from and writing to that region of memory, you can unmap it.
iounmap(ptr);
I hope this helps. I would recommend reading (or at least using as a reference) Linux Device Drivers, 3rd Edition, which can be read online for free.

Resources