Split addresses on 32 and 64 bit platforms
This has to run of 32 and 64 bit kernels.
Anyone know how to get rid of this error on 32 bit platforms /vd80Drvr.c: In function ‘split_address’: /vd80Drvr.c:33: warning: right shift count >= width of type /** * =========================================================== * @brief Given a pointer split it into upr and lwr parts * @param upr address where upper part of arddress will be stored * @param lwr address where lower part of arddress will be stored * @param ptr is the given pointer value to be split */ void split_address(uint32_t *upr, uint32_t *lwr, uintptr_t ptr) { *upr = (uint32_t) (ptr >> 32); *lwr = (uint32_t) (ptr & 0xFFFFFFFF); } |
Code:
*upr = (uint32_t)(((uint64_t)ptr) >> 32); |
Code:
if (sizeof ptr == sizeof *lwr) { |
Thanks,
*upr = (uint32_t)(((uint64_t)ptr) >> 32); I hope this code does not actually copy ptr to a long long on a 32bit platform |
Quote:
Code:
$ cat a.c |
Hi mina86,
that is very interesting, infact I am being told that uintptr_t is not used in the kernel, perhaps your example illustrates why. Thanks |
Off: then please tell me why... if for some reason you have to treat a pointer as an integer, [u]intptr_t is the natural choice (or ptrdiff_t)
|
On a 32bit platform this is what you get.
I need to program a DMA transfer on a tsi148 VME bridge PCI chip It has two registers upr and lwr Code: .file "a.c" .text .p2align 4,,15 .globl split1 .type split1, @function split1: pushl %ebp xorl %edx, %edx movl %esp, %ebp pushl %ebx movl 16(%ebp), %ebx movl 8(%ebp), %ecx movl %ebx, %eax movl %edx, %eax movl %eax, (%ecx) movl 12(%ebp), %eax xorl %edx, %edx movl %ebx, (%eax) popl %ebx popl %ebp ret .size split1, .-split1 .p2align 4,,15 .globl split2 .type split2, @function split2: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax movl $0, (%eax) movl 12(%ebp), %eax movl 16(%ebp), %edx movl %edx, (%eax) popl %ebp ret .size split2, .-split2 .p2align 4,,15 .globl split3 .type split3, @function split3: pushl %ebp xorl %edx, %edx movl %esp, %ebp pushl %ebx movl 16(%ebp), %ebx movl 8(%ebp), %ecx movl %ebx, %eax movl %edx, %eax movl %eax, (%ecx) movl 12(%ebp), %eax xorl %edx, %edx movl %ebx, (%eax) popl %ebx popl %ebp ret .size split3, .-split3 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)" .section .note.GNU-stack,"",@progbits |
You should check how to set 'upr' in 32-bit mode. You might or might not have to do sign-extension, eg 87654321 -> FFFFFFFF87654321
|
If you are writing a Linux driver and need to set up DMA you should use DMA API instead of whatever you seem to be doing. For more information see chapter 15 of LDD3, Documentation/DMA-API.txt and Documentation/DMA-API-HOWTO.txt.
Quote:
Code:
void split_address(uint32_t *upr, uint32_t *lwr, uintptr_t ptr) { Quote:
|
ptrdiff_t is supposed to contain the difference between two pointers. It can even have negative values.
Apparently this type has been misused as a place to store pointers, but actually it is not big enough to hold a full 64 bit address. uintptr_t is defined simply as unsigned long in linux/types.h and there is a load of bullshit declared around it in stdint.h effectivley uintprt_t can always hold a full 64 bit pointer unlike ptrdiff_t and uintptr_t is the same as unsigned long. What I like about uintptr_t is that you declare that you intend it to hold pointers when you use it, although the compiler will not complain if you store any other type in it. Cheers |
This is an email I got from someone who should know. In this case I am going to ignore his advice because as mentioned uintptr_t is just unsigned long.
Quote Email: uintptr_t is never used in the kernel. It's an awful perverse thing. The rule is that "unsigned long" is the type for addresses that must not be dereferenced. morgana% git grep -w uintptr_t | wc -l 426 The figure above is actually zero: morgana% git grep unsigned.long | wc -l 106456 |
Quote:
I don't know how you translated whatever virtual address you had to a physical address, but in 32-bit PAE a physical address is 36-bits, and thus could not have been stored in a pointer. I don't know what kernel support exists to help drivers with DMA (so the driver doesn't need to special case things like PAE), but that seems to be at least mentioned earlier in this thread. In general (though not in detail) I know drivers are not supposed to handle such details themselves, but are supposed to use general purpose constructs. |
> ptrdiff_t is supposed to contain the difference between two pointers.
True > It can even have negative values. Certainly. > Apparently this type has been misused as a place to store pointers, but actually it is not big enough to hold a full 64 bit address. Why do you think so? In 64-bit mode it has to be 64-bit long, to be able to store the difference of any two pointers. > uintptr_t is defined simply as unsigned long in linux/types.h and there is a load of bullshit declared around it in stdint.h Those 'pieces of bullshit' might be useful for those who want to write portable C-code. (eg in LLP64 intptr_t is 'long long', not 'long') > effectively uintprt_t can always hold a full 64 bit pointer unlike ptrdiff_t and uintptr_t is the same as unsigned long. It isn't the case in 32-bit mode: both ptrdiff_t and inptr_t are 32-bit long. > What I like about uintptr_t is that you declare that you intend it to hold pointers when you use it, although the compiler will not complain if you store any other type in it. Very true. |
http://lkml.indiana.edu/hypermail/li...08.2/1018.html
Use of ptrdiff_t in - if (!access_ok(VERIFY_WRITE, u_tmp->rx_buf, u_tmp->len)) + if (!access_ok(VERIFY_WRITE, (u8 __user *) + (ptrdiff_t) u_tmp->rx_buf, + u_tmp->len)) is wrong; for one thing, it's a bad C (it's what uintptr_t is for; in general we are not even promised that ptrdiff_t is large enough to hold a pointer, just enough to hold a difference between two pointers within the same object). For another, it confuses the fsck out of sparse. Use unsigned long or uintptr_t instead. There are several places misusing ptrdiff_t (one - in a very odd way; WTF did that cast to __user ptrdiff_t in ntfs expect to happen, anyway?) Fixed... Signed-off-by: Al Viro <viro@xxxxxxxxxxxxxxxxxx> |
All times are GMT -5. The time now is 08:22 PM. |