diff options
author | Leonid Yegoshin <Leonid.Yegoshin@imgtec.com> | 2013-03-25 14:18:07 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2013-05-09 11:55:19 -0400 |
commit | 34c2f668d0f6b2ca1c076d8170d6cd4f2235a9d4 (patch) | |
tree | 9acdf8f14f17afde3c1f7d4abee55b0c8d3ca0ac /arch/mips/kernel/process.c | |
parent | fb6883e5809c08e43de23581759af4570ca91b0f (diff) |
MIPS: microMIPS: Add unaligned access support.
Add logic needed to handle unaligned accesses in microMIPS mode.
Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r-- | arch/mips/kernel/process.c | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 3be4405c2d14..ef533760d2c8 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c | |||
@@ -7,6 +7,7 @@ | |||
7 | * Copyright (C) 2005, 2006 by Ralf Baechle (ralf@linux-mips.org) | 7 | * Copyright (C) 2005, 2006 by Ralf Baechle (ralf@linux-mips.org) |
8 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. | 8 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. |
9 | * Copyright (C) 2004 Thiemo Seufer | 9 | * Copyright (C) 2004 Thiemo Seufer |
10 | * Copyright (C) 2013 Imagination Technologies Ltd. | ||
10 | */ | 11 | */ |
11 | #include <linux/errno.h> | 12 | #include <linux/errno.h> |
12 | #include <linux/sched.h> | 13 | #include <linux/sched.h> |
@@ -243,34 +244,115 @@ struct mips_frame_info { | |||
243 | 244 | ||
244 | static inline int is_ra_save_ins(union mips_instruction *ip) | 245 | static inline int is_ra_save_ins(union mips_instruction *ip) |
245 | { | 246 | { |
247 | #ifdef CONFIG_CPU_MICROMIPS | ||
248 | union mips_instruction mmi; | ||
249 | |||
250 | /* | ||
251 | * swsp ra,offset | ||
252 | * swm16 reglist,offset(sp) | ||
253 | * swm32 reglist,offset(sp) | ||
254 | * sw32 ra,offset(sp) | ||
255 | * jradiussp - NOT SUPPORTED | ||
256 | * | ||
257 | * microMIPS is way more fun... | ||
258 | */ | ||
259 | if (mm_insn_16bit(ip->halfword[0])) { | ||
260 | mmi.word = (ip->halfword[0] << 16); | ||
261 | return ((mmi.mm16_r5_format.opcode == mm_swsp16_op && | ||
262 | mmi.mm16_r5_format.rt == 31) || | ||
263 | (mmi.mm16_m_format.opcode == mm_pool16c_op && | ||
264 | mmi.mm16_m_format.func == mm_swm16_op)); | ||
265 | } | ||
266 | else { | ||
267 | mmi.halfword[0] = ip->halfword[1]; | ||
268 | mmi.halfword[1] = ip->halfword[0]; | ||
269 | return ((mmi.mm_m_format.opcode == mm_pool32b_op && | ||
270 | mmi.mm_m_format.rd > 9 && | ||
271 | mmi.mm_m_format.base == 29 && | ||
272 | mmi.mm_m_format.func == mm_swm32_func) || | ||
273 | (mmi.i_format.opcode == mm_sw32_op && | ||
274 | mmi.i_format.rs == 29 && | ||
275 | mmi.i_format.rt == 31)); | ||
276 | } | ||
277 | #else | ||
246 | /* sw / sd $ra, offset($sp) */ | 278 | /* sw / sd $ra, offset($sp) */ |
247 | return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) && | 279 | return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) && |
248 | ip->i_format.rs == 29 && | 280 | ip->i_format.rs == 29 && |
249 | ip->i_format.rt == 31; | 281 | ip->i_format.rt == 31; |
282 | #endif | ||
250 | } | 283 | } |
251 | 284 | ||
252 | static inline int is_jal_jalr_jr_ins(union mips_instruction *ip) | 285 | static inline int is_jal_jalr_jr_ins(union mips_instruction *ip) |
253 | { | 286 | { |
287 | #ifdef CONFIG_CPU_MICROMIPS | ||
288 | /* | ||
289 | * jr16,jrc,jalr16,jalr16 | ||
290 | * jal | ||
291 | * jalr/jr,jalr.hb/jr.hb,jalrs,jalrs.hb | ||
292 | * jraddiusp - NOT SUPPORTED | ||
293 | * | ||
294 | * microMIPS is kind of more fun... | ||
295 | */ | ||
296 | union mips_instruction mmi; | ||
297 | |||
298 | mmi.word = (ip->halfword[0] << 16); | ||
299 | |||
300 | if ((mmi.mm16_r5_format.opcode == mm_pool16c_op && | ||
301 | (mmi.mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op) || | ||
302 | ip->j_format.opcode == mm_jal32_op) | ||
303 | return 1; | ||
304 | if (ip->r_format.opcode != mm_pool32a_op || | ||
305 | ip->r_format.func != mm_pool32axf_op) | ||
306 | return 0; | ||
307 | return (((ip->u_format.uimmediate >> 6) & mm_jalr_op) == mm_jalr_op); | ||
308 | #else | ||
254 | if (ip->j_format.opcode == jal_op) | 309 | if (ip->j_format.opcode == jal_op) |
255 | return 1; | 310 | return 1; |
256 | if (ip->r_format.opcode != spec_op) | 311 | if (ip->r_format.opcode != spec_op) |
257 | return 0; | 312 | return 0; |
258 | return ip->r_format.func == jalr_op || ip->r_format.func == jr_op; | 313 | return ip->r_format.func == jalr_op || ip->r_format.func == jr_op; |
314 | #endif | ||
259 | } | 315 | } |
260 | 316 | ||
261 | static inline int is_sp_move_ins(union mips_instruction *ip) | 317 | static inline int is_sp_move_ins(union mips_instruction *ip) |
262 | { | 318 | { |
319 | #ifdef CONFIG_CPU_MICROMIPS | ||
320 | /* | ||
321 | * addiusp -imm | ||
322 | * addius5 sp,-imm | ||
323 | * addiu32 sp,sp,-imm | ||
324 | * jradiussp - NOT SUPPORTED | ||
325 | * | ||
326 | * microMIPS is not more fun... | ||
327 | */ | ||
328 | if (mm_insn_16bit(ip->halfword[0])) { | ||
329 | union mips_instruction mmi; | ||
330 | |||
331 | mmi.word = (ip->halfword[0] << 16); | ||
332 | return ((mmi.mm16_r3_format.opcode == mm_pool16d_op && | ||
333 | mmi.mm16_r3_format.simmediate && mm_addiusp_func) || | ||
334 | (mmi.mm16_r5_format.opcode == mm_pool16d_op && | ||
335 | mmi.mm16_r5_format.rt == 29)); | ||
336 | } | ||
337 | return (ip->mm_i_format.opcode == mm_addiu32_op && | ||
338 | ip->mm_i_format.rt == 29 && ip->mm_i_format.rs == 29); | ||
339 | #else | ||
263 | /* addiu/daddiu sp,sp,-imm */ | 340 | /* addiu/daddiu sp,sp,-imm */ |
264 | if (ip->i_format.rs != 29 || ip->i_format.rt != 29) | 341 | if (ip->i_format.rs != 29 || ip->i_format.rt != 29) |
265 | return 0; | 342 | return 0; |
266 | if (ip->i_format.opcode == addiu_op || ip->i_format.opcode == daddiu_op) | 343 | if (ip->i_format.opcode == addiu_op || ip->i_format.opcode == daddiu_op) |
267 | return 1; | 344 | return 1; |
345 | #endif | ||
268 | return 0; | 346 | return 0; |
269 | } | 347 | } |
270 | 348 | ||
271 | static int get_frame_info(struct mips_frame_info *info) | 349 | static int get_frame_info(struct mips_frame_info *info) |
272 | { | 350 | { |
351 | #ifdef CONFIG_CPU_MICROMIPS | ||
352 | union mips_instruction *ip = (void *) (((char *) info->func) - 1); | ||
353 | #else | ||
273 | union mips_instruction *ip = info->func; | 354 | union mips_instruction *ip = info->func; |
355 | #endif | ||
274 | unsigned max_insns = info->func_size / sizeof(union mips_instruction); | 356 | unsigned max_insns = info->func_size / sizeof(union mips_instruction); |
275 | unsigned i; | 357 | unsigned i; |
276 | 358 | ||
@@ -290,7 +372,26 @@ static int get_frame_info(struct mips_frame_info *info) | |||
290 | break; | 372 | break; |
291 | if (!info->frame_size) { | 373 | if (!info->frame_size) { |
292 | if (is_sp_move_ins(ip)) | 374 | if (is_sp_move_ins(ip)) |
375 | { | ||
376 | #ifdef CONFIG_CPU_MICROMIPS | ||
377 | if (mm_insn_16bit(ip->halfword[0])) | ||
378 | { | ||
379 | unsigned short tmp; | ||
380 | |||
381 | if (ip->halfword[0] & mm_addiusp_func) | ||
382 | { | ||
383 | tmp = (((ip->halfword[0] >> 1) & 0x1ff) << 2); | ||
384 | info->frame_size = -(signed short)(tmp | ((tmp & 0x100) ? 0xfe00 : 0)); | ||
385 | } else { | ||
386 | tmp = (ip->halfword[0] >> 1); | ||
387 | info->frame_size = -(signed short)(tmp & 0xf); | ||
388 | } | ||
389 | ip = (void *) &ip->halfword[1]; | ||
390 | ip--; | ||
391 | } else | ||
392 | #endif | ||
293 | info->frame_size = - ip->i_format.simmediate; | 393 | info->frame_size = - ip->i_format.simmediate; |
394 | } | ||
294 | continue; | 395 | continue; |
295 | } | 396 | } |
296 | if (info->pc_offset == -1 && is_ra_save_ins(ip)) { | 397 | if (info->pc_offset == -1 && is_ra_save_ins(ip)) { |