Linux内核判断大小端,linux kernel 如何处理大小端

暂时在用MPC8309,不太清楚大小端内核是什么时候给转的。

今天看了关于readl和writel具体实现的文章

今天就主要来分析下readl/writel如何实现高效的数据swap和寄存器读写。我们就以readl为例,针对big-endian处理器,如何来对寄存器数据进行处理。

kernel下readl定义如下,在include/asm-generic/io.h

#define readw(addr) __le32_to_cpu(__raw_readw(addr))

__raw_readl是最底层的寄存器读写函数,很简单,就从直接获取寄存器数据。来看__le32_to_cpu的实现,该函数针对字节序有不同的实现,对于小端处理器,在./include/linux/byteorder/little_endian.h中,如下:

#define __le32_to_cpu(x) ((__force __u32)(__le32)(x))

相当于什么都没做。而对于大端处理器,在./include/linux/byteorder/big_endian.h中,如下:

#define __le32_to_cpu(x) __swab32((__force __u32)(__le32)(x))

看字面意思也可以看出,__swab32实现数据翻转。等下我们就来分析__swab32的实现,精髓就在这个函数。

但是这之前先考虑一个问题,对于不同CPU,如arm mips ppc,怎么来选择使用little_endian.h还是big_endian.h的呢。

答案是,针对不同处理器平台,有arch/xxx/include/asm/byteorder.h头文件,来看下arm mips ppc的byteorder.h分别是什么。

arch/arm/include/asm/byteorder.h

*  arch/arm/include/asm/byteorder.h

*

* ARM Endian-ness.  In little endian mode, the data bus is connected such

* that byte accesses appear as:

*  0 = d0...d7, 1 = d8...d15, 2 = d16...d23, 3 = d24...d31

* and word accesses (data or instruction) appear as:

*  d0...d31

*

* When in big endian mode, byte accesses appear as:

*  0 = d24...d31, 1 = d16...d23, 2 = d8...d15, 3 = d0...d7

* and word accesses (data or instruction) appear as:

*  d0...d31

*/

#ifndef __ASM_ARM_BYTEORDER_H

#define __ASM_ARM_BYTEORDER_H

#ifdef __ARMEB__

#include 

#else

#include 

#endif

#endif

arch/mips/include/asm/byteorder.h

/*

* This file is subject to the terms and conditions of the GNU General Public

* License.  See the file "COPYING" in the main directory of this archive

* for more details.

*

* Copyright (C) 1996, 99, 2003 by Ralf Baechle

*/

#ifndef _ASM_BYTEORDER_H

#define _ASM_BYTEORDER_H

#if defined(__MIPSEB__)

#include 

#elif defined(__MIPSEL__)

#include 

#else

# error "MIPS, but neither __MIPSEB__, nor __MIPSEL__???"

#endif

#endif /* _ASM_BYTEORDER_H */

arch/powerpc/include/asm/byteorder.h

#ifndef _ASM_POWERPC_BYTEORDER_H

#define _ASM_POWERPC_BYTEORDER_H

/*

* This program is free software; you can redistribute it and/or

* modify it under the terms of the GNU General Public License

* as published by the Free Software Foundation; either version

* 2 of the License, or (at your option) any later version.

*/

#include 

#endif /* _ASM_POWERPC_BYTEORDER_H */

可以看出arm mips在kernel下大小端都支持,arm mips也的确是可以选择处理器字节序。ppc仅支持big-endian。(其实ppc也是支持选择字节序的)

各个处理器平台的byteorder.h将littlie_endian.h/big_endian.h又包了一层,我们在编写driver时不需要关心处理器的字节序,只需要包含byteorder.h即可。

接下来看下最关键的__swab32函数,如下:

在include/linux/swab.h中

/**

* __swab32 - return a byteswapped 32-bit value

* @x: value to byteswap

*/

#define __swab32(x)             \

(__builtin_constant_p((__u32)(x)) ? \

___constant_swab32(x) :         \

__fswab32(x))

宏定义展开,是一个条件判断符。

__builtin_constant_p是一个gcc的内建函数, 用于判断一个值在编译时是否是常数,如果参数是常数,函数返回 1,否则返回 0。

如果数据是常数,则__constant_swab32,实现如下:

#define ___constant_swab32(x) ((__u32)(             \

(((__u32)(x) & (__u32)0x000000ffUL) <

(((__u32)(x) & (__u32)0x0000ff00UL) <

(((__u32)(x) & (__u32)0x00ff0000UL) >>  8) |        \

(((__u32)(x) & (__u32)0xff000000UL) >> 24)))

对于常数数据,采用的是普通的位移然后拼接的方法,对于常数,这样的消耗是有必要的(这是kernel的解释,不是很理解)

如果数据是运行时计算数据,则使用__fswab32,实现如下:

static inline __attribute_const__ __u32 __fswab32(__u32 val)

{

#ifdef __arch_swab32

return __arch_swab32(val);

#else

return ___constant_swab32(val);

#endif

}

如果未定义__arch_swab32,则还是采用__constant_swab32方法翻转数据,但是arm mips ppc都定义了各自平台的__arch_swab32,来实现一个针对自己平台的高效的swap,分别定义如下:

arch/arm/include/asm/swab.h

static inline __attribute_const__ __u32 __arch_swab32(__u32 x)

{

__asm__ ("rev %0, %1" : "=r" (x) : "r" (x));

return x;

}

arch/mips/include/asm/swab.h

static inline __attribute_const__ __u32 __arch_swab32(__u32 x)

{

__asm__(

"   wsbh    %0, %1          \n"

"   rotr    %0, %0, 16      \n"

: "=r" (x)

: "r" (x));

return x;

}

arch/powerpc/include/asm/swab.h

static inline __attribute_const__ __u32 __arch_swab32(__u32 value)

{

__u32 result;

__asm__("rlwimi %0,%1,24,16,23\n\t"

"rlwimi %0,%1,8,8,15\n\t"

"rlwimi %0,%1,24,0,7"

: "=r" (result)

: "r" (value), "0" (value >> 24));

return result;

}

可以看出,arm使用1条指令(rev数据翻转指令),mips使用2条指令(wsbh rotr数据交换指令),ppc使用3条指令(rlwimi数据位移指令),来完成了32 bit数据的翻转。这相对于普通的位移拼接的方法要高效的多!

其实从函数名__fswab也可以看出是要实现fast swap的。

原文:http://www.cnblogs.com/yangv/p/5553717.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值