Commit 1efea40d authored by Chris Metcalf's avatar Chris Metcalf

arch/tile: support building big-endian kernel

The toolchain supports big-endian mode now, so add support for building
the kernel to run big-endian as well.
Signed-off-by: default avatarChris Metcalf <cmetcalf@tilera.com>
parent 73636b1a
/*
* Copyright 2011 Tilera Corporation. All Rights Reserved.
*
* 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, version 2.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for
* more details.
*/
#if defined (__BIG_ENDIAN__)
#include <linux/byteorder/big_endian.h>
#elif defined (__LITTLE_ENDIAN__)
#include <linux/byteorder/little_endian.h> #include <linux/byteorder/little_endian.h>
#else
#error "__BIG_ENDIAN__ or __LITTLE_ENDIAN__ must be defined."
#endif
...@@ -44,7 +44,11 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; ...@@ -44,7 +44,11 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
#else #else
#define ELF_CLASS ELFCLASS32 #define ELF_CLASS ELFCLASS32
#endif #endif
#ifdef __BIG_ENDIAN__
#define ELF_DATA ELFDATA2MSB
#else
#define ELF_DATA ELFDATA2LSB #define ELF_DATA ELFDATA2LSB
#endif
/* /*
* There seems to be a bug in how compat_binfmt_elf.c works: it * There seems to be a bug in how compat_binfmt_elf.c works: it
...@@ -59,6 +63,7 @@ enum { ELF_ARCH = CHIP_ELF_TYPE() }; ...@@ -59,6 +63,7 @@ enum { ELF_ARCH = CHIP_ELF_TYPE() };
*/ */
#define elf_check_arch(x) \ #define elf_check_arch(x) \
((x)->e_ident[EI_CLASS] == ELF_CLASS && \ ((x)->e_ident[EI_CLASS] == ELF_CLASS && \
(x)->e_ident[EI_DATA] == ELF_DATA && \
(x)->e_machine == CHIP_ELF_TYPE()) (x)->e_machine == CHIP_ELF_TYPE())
/* The module loader only handles a few relocation types. */ /* The module loader only handles a few relocation types. */
......
...@@ -494,11 +494,16 @@ int hv_confstr(HV_ConfstrQuery query, HV_VirtAddr buf, int len); ...@@ -494,11 +494,16 @@ int hv_confstr(HV_ConfstrQuery query, HV_VirtAddr buf, int len);
/** Tile coordinate */ /** Tile coordinate */
typedef struct typedef struct
{ {
#ifndef __BIG_ENDIAN__
/** X coordinate, relative to supervisor's top-left coordinate */ /** X coordinate, relative to supervisor's top-left coordinate */
int x; int x;
/** Y coordinate, relative to supervisor's top-left coordinate */ /** Y coordinate, relative to supervisor's top-left coordinate */
int y; int y;
#else
int y;
int x;
#endif
} HV_Coord; } HV_Coord;
...@@ -986,8 +991,13 @@ HV_VirtAddrRange hv_inquire_virtual(int idx); ...@@ -986,8 +991,13 @@ HV_VirtAddrRange hv_inquire_virtual(int idx);
/** A range of ASID values. */ /** A range of ASID values. */
typedef struct typedef struct
{ {
#ifndef __BIG_ENDIAN__
HV_ASID start; /**< First ASID in the range. */ HV_ASID start; /**< First ASID in the range. */
unsigned int size; /**< Number of ASIDs. Zero for an invalid range. */ unsigned int size; /**< Number of ASIDs. Zero for an invalid range. */
#else
unsigned int size; /**< Number of ASIDs. Zero for an invalid range. */
HV_ASID start; /**< First ASID in the range. */
#endif
} HV_ASIDRange; } HV_ASIDRange;
/** Returns information about a range of ASIDs. /** Returns information about a range of ASIDs.
...@@ -1308,6 +1318,7 @@ typedef enum ...@@ -1308,6 +1318,7 @@ typedef enum
/** Message recipient. */ /** Message recipient. */
typedef struct typedef struct
{ {
#ifndef __BIG_ENDIAN__
/** X coordinate, relative to supervisor's top-left coordinate */ /** X coordinate, relative to supervisor's top-left coordinate */
unsigned int x:11; unsigned int x:11;
...@@ -1316,6 +1327,11 @@ typedef struct ...@@ -1316,6 +1327,11 @@ typedef struct
/** Status of this recipient */ /** Status of this recipient */
HV_Recip_State state:10; HV_Recip_State state:10;
#else //__BIG_ENDIAN__
HV_Recip_State state:10;
unsigned int y:11;
unsigned int x:11;
#endif
} HV_Recipient; } HV_Recipient;
/** Send a message to a set of recipients. /** Send a message to a set of recipients.
......
...@@ -159,7 +159,17 @@ int apply_relocate_add(Elf_Shdr *sechdrs, ...@@ -159,7 +159,17 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
switch (ELF_R_TYPE(rel[i].r_info)) { switch (ELF_R_TYPE(rel[i].r_info)) {
#define MUNGE(func) (*location = ((*location & ~func(-1)) | func(value))) #ifdef __LITTLE_ENDIAN
# define MUNGE(func) \
(*location = ((*location & ~func(-1)) | func(value)))
#else
/*
* Instructions are always little-endian, so when we read them as data,
* we have to swap them around before and after modifying them.
*/
# define MUNGE(func) \
(*location = swab64((swab64(*location) & ~func(-1)) | func(value)))
#endif
#ifndef __tilegx__ #ifndef __tilegx__
case R_TILE_32: case R_TILE_32:
......
...@@ -172,9 +172,6 @@ static tile_bundle_bits rewrite_load_store_unaligned( ...@@ -172,9 +172,6 @@ static tile_bundle_bits rewrite_load_store_unaligned(
return (tilepro_bundle_bits) 0; return (tilepro_bundle_bits) 0;
} }
#ifndef __LITTLE_ENDIAN
# error We assume little-endian representation with copy_xx_user size 2 here
#endif
/* Handle unaligned load/store */ /* Handle unaligned load/store */
if (mem_op == MEMOP_LOAD || mem_op == MEMOP_LOAD_POSTINCR) { if (mem_op == MEMOP_LOAD || mem_op == MEMOP_LOAD_POSTINCR) {
unsigned short val_16; unsigned short val_16;
...@@ -195,8 +192,19 @@ static tile_bundle_bits rewrite_load_store_unaligned( ...@@ -195,8 +192,19 @@ static tile_bundle_bits rewrite_load_store_unaligned(
state->update = 1; state->update = 1;
} }
} else { } else {
unsigned short val_16;
val = (val_reg == TREG_ZERO) ? 0 : regs->regs[val_reg]; val = (val_reg == TREG_ZERO) ? 0 : regs->regs[val_reg];
err = copy_to_user(addr, &val, size); switch (size) {
case 2:
val_16 = val;
err = copy_to_user(addr, &val_16, sizeof(val_16));
break;
case 4:
err = copy_to_user(addr, &val, sizeof(val));
break;
default:
BUG();
}
} }
if (err) { if (err) {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/module.h> #include <linux/module.h>
#include "string-endian.h"
void *memchr(const void *s, int c, size_t n) void *memchr(const void *s, int c, size_t n)
{ {
...@@ -39,11 +40,8 @@ void *memchr(const void *s, int c, size_t n) ...@@ -39,11 +40,8 @@ void *memchr(const void *s, int c, size_t n)
/* Read the first word, but munge it so that bytes before the array /* Read the first word, but munge it so that bytes before the array
* will not match goal. * will not match goal.
*
* Note that this shift count expression works because we know
* shift counts are taken mod 64.
*/ */
before_mask = (1ULL << (s_int << 3)) - 1; before_mask = MASK(s_int);
v = (*p | before_mask) ^ (goal & before_mask); v = (*p | before_mask) ^ (goal & before_mask);
/* Compute the address of the last byte. */ /* Compute the address of the last byte. */
...@@ -65,7 +63,7 @@ void *memchr(const void *s, int c, size_t n) ...@@ -65,7 +63,7 @@ void *memchr(const void *s, int c, size_t n)
/* We found a match, but it might be in a byte past the end /* We found a match, but it might be in a byte past the end
* of the array. * of the array.
*/ */
ret = ((char *)p) + (__insn_ctz(bits) >> 3); ret = ((char *)p) + (CFZ(bits) >> 3);
return (ret <= last_byte_ptr) ? ret : NULL; return (ret <= last_byte_ptr) ? ret : NULL;
} }
EXPORT_SYMBOL(memchr); EXPORT_SYMBOL(memchr);
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/module.h> #include <linux/module.h>
#define __memcpy memcpy
/* EXPORT_SYMBOL() is in arch/tile/lib/exports.c since this should be asm. */ /* EXPORT_SYMBOL() is in arch/tile/lib/exports.c since this should be asm. */
/* Must be 8 bytes in size. */ /* Must be 8 bytes in size. */
...@@ -188,6 +187,7 @@ int USERCOPY_FUNC(void *__restrict dstv, const void *__restrict srcv, size_t n) ...@@ -188,6 +187,7 @@ int USERCOPY_FUNC(void *__restrict dstv, const void *__restrict srcv, size_t n)
/* n != 0 if we get here. Write out any trailing bytes. */ /* n != 0 if we get here. Write out any trailing bytes. */
dst1 = (char *)dst8; dst1 = (char *)dst8;
#ifndef __BIG_ENDIAN__
if (n & 4) { if (n & 4) {
ST4((uint32_t *)dst1, final); ST4((uint32_t *)dst1, final);
dst1 += 4; dst1 += 4;
...@@ -202,11 +202,30 @@ int USERCOPY_FUNC(void *__restrict dstv, const void *__restrict srcv, size_t n) ...@@ -202,11 +202,30 @@ int USERCOPY_FUNC(void *__restrict dstv, const void *__restrict srcv, size_t n)
} }
if (n) if (n)
ST1((uint8_t *)dst1, final); ST1((uint8_t *)dst1, final);
#else
if (n & 4) {
ST4((uint32_t *)dst1, final >> 32);
dst1 += 4;
}
else
{
final >>= 32;
}
if (n & 2) {
ST2((uint16_t *)dst1, final >> 16);
dst1 += 2;
}
else
{
final >>= 16;
}
if (n & 1)
ST1((uint8_t *)dst1, final >> 8);
#endif
return RETVAL; return RETVAL;
} }
#ifdef USERCOPY_FUNC #ifdef USERCOPY_FUNC
#undef ST1 #undef ST1
#undef ST2 #undef ST2
......
...@@ -15,8 +15,7 @@ ...@@ -15,8 +15,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/module.h> #include <linux/module.h>
#include "string-endian.h"
#undef strchr
char *strchr(const char *s, int c) char *strchr(const char *s, int c)
{ {
...@@ -33,13 +32,9 @@ char *strchr(const char *s, int c) ...@@ -33,13 +32,9 @@ char *strchr(const char *s, int c)
* match neither zero nor goal (we make sure the high bit of each * match neither zero nor goal (we make sure the high bit of each
* byte is 1, and the low 7 bits are all the opposite of the goal * byte is 1, and the low 7 bits are all the opposite of the goal
* byte). * byte).
*
* Note that this shift count expression works because we know shift
* counts are taken mod 64.
*/ */
const uint64_t before_mask = (1ULL << (s_int << 3)) - 1; const uint64_t before_mask = MASK(s_int);
uint64_t v = (*p | before_mask) ^ uint64_t v = (*p | before_mask) ^ (goal & __insn_v1shrui(before_mask, 1));
(goal & __insn_v1shrsi(before_mask, 1));
uint64_t zero_matches, goal_matches; uint64_t zero_matches, goal_matches;
while (1) { while (1) {
...@@ -55,8 +50,8 @@ char *strchr(const char *s, int c) ...@@ -55,8 +50,8 @@ char *strchr(const char *s, int c)
v = *++p; v = *++p;
} }
z = __insn_ctz(zero_matches); z = CFZ(zero_matches);
g = __insn_ctz(goal_matches); g = CFZ(goal_matches);
/* If we found c before '\0' we got a match. Note that if c == '\0' /* If we found c before '\0' we got a match. Note that if c == '\0'
* then g == z, and we correctly return the address of the '\0' * then g == z, and we correctly return the address of the '\0'
......
/*
* Copyright 2011 Tilera Corporation. All Rights Reserved.
*
* 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, version 2.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for
* more details.
*
* Provide a mask based on the pointer alignment that
* sets up non-zero bytes before the beginning of the string.
* The MASK expression works because shift counts are taken mod 64.
* Also, specify how to count "first" and "last" bits
* when the bits have been read as a word.
*/
#include <asm/byteorder.h>
#ifdef __LITTLE_ENDIAN
#define MASK(x) (__insn_shl(1ULL, (x << 3)) - 1)
#define NULMASK(x) ((2ULL << x) - 1)
#define CFZ(x) __insn_ctz(x)
#define REVCZ(x) __insn_clz(x)
#else
#define MASK(x) (__insn_shl(-2LL, ((-x << 3) - 1)))
#define NULMASK(x) (-2LL << (63 - x))
#define CFZ(x) __insn_clz(x)
#define REVCZ(x) __insn_ctz(x)
#endif
...@@ -15,8 +15,7 @@ ...@@ -15,8 +15,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/module.h> #include <linux/module.h>
#include "string-endian.h"
#undef strlen
size_t strlen(const char *s) size_t strlen(const char *s)
{ {
...@@ -24,15 +23,13 @@ size_t strlen(const char *s) ...@@ -24,15 +23,13 @@ size_t strlen(const char *s)
const uintptr_t s_int = (uintptr_t) s; const uintptr_t s_int = (uintptr_t) s;
const uint64_t *p = (const uint64_t *)(s_int & -8); const uint64_t *p = (const uint64_t *)(s_int & -8);
/* Read the first word, but force bytes before the string to be nonzero. /* Read and MASK the first word. */
* This expression works because we know shift counts are taken mod 64. uint64_t v = *p | MASK(s_int);
*/
uint64_t v = *p | ((1ULL << (s_int << 3)) - 1);
uint64_t bits; uint64_t bits;
while ((bits = __insn_v1cmpeqi(v, 0)) == 0) while ((bits = __insn_v1cmpeqi(v, 0)) == 0)
v = *++p; v = *++p;
return ((const char *)p) + (__insn_ctz(bits) >> 3) - s; return ((const char *)p) + (CFZ(bits) >> 3) - s;
} }
EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(strlen);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment