Having this functionality partially "broken" opens the door for subtle bugs in peripheral drivers for AT91 platform since it not straight out obvious that IS_ERR might return a false positive. It also makes it much harder to judge the correctness of the driver code, for example it is perfectly fine to use IS_ERR in at91-i2c.c since I2C controller's register file is located at 0xFFFA_C000 (which it doesn't but that's the subject for another patch), however one couldn't help but wonder how the code it sam9_smc.c could possibly work given how that module is located at 0xFFFF_EC00. Signed-off-by: Andrey Smirnov <andrew.smirnov@xxxxxxxxx> --- scripts/include/linux/err.h | 66 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/scripts/include/linux/err.h b/scripts/include/linux/err.h index bdc3dd8..f6ce0d0 100644 --- a/scripts/include/linux/err.h +++ b/scripts/include/linux/err.h @@ -29,6 +29,69 @@ */ #define MAX_ERRNO 4095 +#ifdef CONFIG_ARCH_AT91 + +/* + * AT91 maps all of its peripherals' register files into last 256MB of + * address space. This means that if no appropriate action is taken + * ERR_PTR et al. would not work. We also don't have the luxury of + * guaranted to be enabled MMU, so remapping is not an option. Instead + * we do a little bit of additional arithmetic and "move" all of the + * error codes to the page right before AT91's peripheral memory starts + * (0xEFFF_0000 to 0xEFFF_FFFF) + */ + +static inline bool IS_ERR_VALUE(unsigned long x) +{ + return x >= 0xEFFF0000 && + x <= 0xEFFFFFFF; +} + +static inline void * __must_check ERR_PTR(long error_) +{ + /* + * We need to remap all errnos from 0xFFFF_0000 - 0xFFFF_FFFF + * to 0xEFFF_0000 - 0xEFFF_FFFF + * + * Given that + * + * errno_ == 0xFFFF_FFFF - (abs(errno_) - 1) + * + * and what we want it to be is + * + * errno_ == 0xEFFF_FFFF - (abs(errno_) - 1) + * + * desired remapping can be acheived by the following code: + */ + unsigned long e = error_; + + BUG_ON(error_ < -MAX_ERRNO); + /* + * Since we know that e is not going to be smaller then + * 0xFFFF_0000 instead of substracting 0x1000_0000 from 'e' we + * can just "convert" most significant nibble of the value to + * 'e' using bitwise and + */ + e &= 0xEFFFFFFF; + + return (void *) e; +} + +static inline long __must_check PTR_ERR(__force const void *ptr) +{ + unsigned long e = (unsigned long) error; + + /* + * This is the "inverse transformation" corresponding to the + * code above + */ + e |= 0xF0000000; + + return (long) e; +} + +#else + #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO) static inline void * __must_check ERR_PTR(long error_) @@ -41,6 +104,9 @@ static inline long __must_check PTR_ERR(__force const void *ptr) return (long) ptr; } +#endif + + static inline bool __must_check IS_ERR(__force const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); -- 2.5.0 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox