LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 02-26-2013, 11:01 AM   #1
Julian Lewis
LQ Newbie
 
Registered: Apr 2012
Location: Geneva, Switzerland
Distribution: Scientific Linux / Red Hat - Ubuntu
Posts: 12

Rep: Reputation: Disabled
Question 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);
}
 
Old 02-26-2013, 11:40 AM   #2
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,862
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
Code:
 *upr = (uint32_t)(((uint64_t)ptr) >> 32);
 
1 members found this post helpful.
Old 02-26-2013, 04:46 PM   #3
mina86
Member
 
Registered: Aug 2008
Distribution: Debian
Posts: 517

Rep: Reputation: 229Reputation: 229Reputation: 229
Code:
if (sizeof ptr == sizeof *lwr) {
    *lwr = (uint32_t)ptr;
    *uwr = 0;
} else {
    … what you have …
}
 
1 members found this post helpful.
Old 02-27-2013, 02:26 AM   #4
Julian Lewis
LQ Newbie
 
Registered: Apr 2012
Location: Geneva, Switzerland
Distribution: Scientific Linux / Red Hat - Ubuntu
Posts: 12

Original Poster
Rep: Reputation: Disabled
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
 
Old 02-27-2013, 08:00 AM   #5
mina86
Member
 
Registered: Aug 2008
Distribution: Debian
Posts: 517

Rep: Reputation: 229Reputation: 229Reputation: 229
Quote:
Originally Posted by Julian Lewis View Post
I hope this code does not actually copy ptr to a long long on a 32bit platform
Why won't you check for yourself?
Code:
$ cat a.c
#include <stdint.h>

void split1(uint32_t *upr, uint32_t *lwr, uintptr_t ptr) {
	*upr = (uint32_t)((uint64_t)ptr >> 32);
	*lwr = (uint32_t)ptr;
}

void split2(uint32_t *upr, uint32_t *lwr, uintptr_t ptr) {
	if (sizeof ptr == sizeof *lwr) {
		*upr = 0;
	} else {
		*upr = (uint32_t)(ptr >> 32);
	}
	*lwr = (uint32_t)ptr;
}

void split3(uint32_t *upr, uint32_t *lwr, uint32_t ptr) {
	*upr = (uint32_t)((uint64_t)ptr >> 32);
	*lwr = (uint32_t)ptr;
}
$ gcc -O2 -o a.S -S a.c
$ cat a.S
	.file	"a.c"
	.text
	.p2align 4,,15
	.globl	split1
	.type	split1, @function
split1:
.LFB0:
	.cfi_startproc
	movq	%rdx, %rax
	shrq	$32, %rax
	movl	%eax, (%rdi)
	movl	%edx, (%rsi)
	ret
	.cfi_endproc
.LFE0:
	.size	split1, .-split1
	.p2align 4,,15
	.globl	split2
	.type	split2, @function
split2:
.LFB1:
	.cfi_startproc
	movq	%rdx, %rax
	shrq	$32, %rax
	movl	%eax, (%rdi)
	movl	%edx, (%rsi)
	ret
	.cfi_endproc
.LFE1:
	.size	split2, .-split2
	.p2align 4,,15
	.globl	split3
	.type	split3, @function
split3:
.LFB2:
	.cfi_startproc
	movl	$0, (%rdi)
	movl	%edx, (%rsi)
	ret
	.cfi_endproc
.LFE2:
	.size	split3, .-split3
	.ident	"GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
	.section	.note.GNU-stack,"",@progbits
Repeating for different optimisation flags is left as exercise for the reader.
 
1 members found this post helpful.
Old 02-27-2013, 08:27 AM   #6
Julian Lewis
LQ Newbie
 
Registered: Apr 2012
Location: Geneva, Switzerland
Distribution: Scientific Linux / Red Hat - Ubuntu
Posts: 12

Original Poster
Rep: Reputation: Disabled
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
 
Old 02-27-2013, 09:09 AM   #7
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,862
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
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)
 
Old 02-27-2013, 09:13 AM   #8
Julian Lewis
LQ Newbie
 
Registered: Apr 2012
Location: Geneva, Switzerland
Distribution: Scientific Linux / Red Hat - Ubuntu
Posts: 12

Original Poster
Rep: Reputation: Disabled
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
 
Old 02-27-2013, 09:57 AM   #9
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,862
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
You should check how to set 'upr' in 32-bit mode. You might or might not have to do sign-extension, eg 87654321 -> FFFFFFFF87654321
 
Old 02-27-2013, 10:22 AM   #10
mina86
Member
 
Registered: Aug 2008
Distribution: Debian
Posts: 517

Rep: Reputation: 229Reputation: 229Reputation: 229
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:
Originally Posted by Julian Lewis View Post
On a 32bit platform this is what you get.
I bet any kind of optimisation will get rid of the differences, but it seems that my solution is best if no optimisation is enabled. Still it does not get rid of the warning as I hoped it would. So I guess it's either casting up or preprocessor:

Code:
void split_address(uint32_t *upr, uint32_t *lwr, uintptr_t ptr) {
#if UINTPTR_MAX == UINT32_MAX
    *upr = 0;  /* note NevemTeve comment though */
#else
    *upr = ptr >> 32;
#endif
    *lwr = ptr;
}
Quote:
Originally Posted by NevemTeve View Post
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)
In Linux kernel, long or unsigned long is usually used if pointer needs to be stored as an integer. It so happens, that on all architectures Linux runs on, long can store a pointer.

Last edited by mina86; 02-27-2013 at 10:36 AM.
 
2 members found this post helpful.
Old 02-27-2013, 10:28 AM   #11
Julian Lewis
LQ Newbie
 
Registered: Apr 2012
Location: Geneva, Switzerland
Distribution: Scientific Linux / Red Hat - Ubuntu
Posts: 12

Original Poster
Rep: Reputation: Disabled
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
 
Old 02-27-2013, 10:33 AM   #12
Julian Lewis
LQ Newbie
 
Registered: Apr 2012
Location: Geneva, Switzerland
Distribution: Scientific Linux / Red Hat - Ubuntu
Posts: 12

Original Poster
Rep: Reputation: Disabled
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
 
Old 02-27-2013, 10:42 AM   #13
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by Julian Lewis View Post
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
Do you expect to support 32-bit PAE?

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.
 
Old 02-27-2013, 11:25 AM   #14
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,862
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
> 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.

Last edited by NevemTeve; 02-27-2013 at 11:26 AM.
 
Old 03-01-2013, 02:52 AM   #15
Julian Lewis
LQ Newbie
 
Registered: Apr 2012
Location: Geneva, Switzerland
Distribution: Scientific Linux / Red Hat - Ubuntu
Posts: 12

Original Poster
Rep: Reputation: Disabled
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>
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Adobe releases preview of flash 64 bit for all platforms jlo_sandog Linux - Software 1 09-17-2010 10:07 PM
Need sendmail to split single envelope into multiple To: addresses Dan_in_NH Linux - Server 1 08-04-2010 04:07 PM
LXer: Sigrity Supports 64-bit Linux Computing Platforms LXer Syndicated Linux News 0 01-15-2007 09:54 PM
pci 16-bit field addresses drminix Linux - Hardware 0 06-08-2005 12:54 AM
Why IP addresses are represented in 32-bit patterns? velan Debian 1 04-16-2004 02:06 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 08:56 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration