diff options
author | Steven J. Hill <Steven.Hill@imgtec.com> | 2013-03-25 14:46:15 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2013-05-09 11:55:20 -0400 |
commit | 451b001b05551a2bf9ec4c2293f20e34a4520701 (patch) | |
tree | e9134d4a25142d616b0bd19d096b85a753d02c44 /arch | |
parent | 8508488fe7028b95bc86c7447c205fbc765cc4cf (diff) |
MIPS: MIPS16e: Add unaligned access support.
Add logic needed to handle unaligned accesses in MIPS16e mode.
Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/mips/kernel/unaligned.c | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c index 0213906e3f82..203d8857070d 100644 --- a/arch/mips/kernel/unaligned.c +++ b/arch/mips/kernel/unaligned.c | |||
@@ -1304,6 +1304,250 @@ sigill: | |||
1304 | force_sig(SIGILL, current); | 1304 | force_sig(SIGILL, current); |
1305 | } | 1305 | } |
1306 | 1306 | ||
1307 | static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr) | ||
1308 | { | ||
1309 | unsigned long value; | ||
1310 | unsigned int res; | ||
1311 | int reg; | ||
1312 | unsigned long orig31; | ||
1313 | u16 __user *pc16; | ||
1314 | unsigned long origpc; | ||
1315 | union mips16e_instruction mips16inst, oldinst; | ||
1316 | |||
1317 | origpc = regs->cp0_epc; | ||
1318 | orig31 = regs->regs[31]; | ||
1319 | pc16 = (unsigned short __user *)msk_isa16_mode(origpc); | ||
1320 | /* | ||
1321 | * This load never faults. | ||
1322 | */ | ||
1323 | __get_user(mips16inst.full, pc16); | ||
1324 | oldinst = mips16inst; | ||
1325 | |||
1326 | /* skip EXTEND instruction */ | ||
1327 | if (mips16inst.ri.opcode == MIPS16e_extend_op) { | ||
1328 | pc16++; | ||
1329 | __get_user(mips16inst.full, pc16); | ||
1330 | } else if (delay_slot(regs)) { | ||
1331 | /* skip jump instructions */ | ||
1332 | /* JAL/JALX are 32 bits but have OPCODE in first short int */ | ||
1333 | if (mips16inst.ri.opcode == MIPS16e_jal_op) | ||
1334 | pc16++; | ||
1335 | pc16++; | ||
1336 | if (get_user(mips16inst.full, pc16)) | ||
1337 | goto sigbus; | ||
1338 | } | ||
1339 | |||
1340 | switch (mips16inst.ri.opcode) { | ||
1341 | case MIPS16e_i64_op: /* I64 or RI64 instruction */ | ||
1342 | switch (mips16inst.i64.func) { /* I64/RI64 func field check */ | ||
1343 | case MIPS16e_ldpc_func: | ||
1344 | case MIPS16e_ldsp_func: | ||
1345 | reg = reg16to32[mips16inst.ri64.ry]; | ||
1346 | goto loadDW; | ||
1347 | |||
1348 | case MIPS16e_sdsp_func: | ||
1349 | reg = reg16to32[mips16inst.ri64.ry]; | ||
1350 | goto writeDW; | ||
1351 | |||
1352 | case MIPS16e_sdrasp_func: | ||
1353 | reg = 29; /* GPRSP */ | ||
1354 | goto writeDW; | ||
1355 | } | ||
1356 | |||
1357 | goto sigbus; | ||
1358 | |||
1359 | case MIPS16e_swsp_op: | ||
1360 | case MIPS16e_lwpc_op: | ||
1361 | case MIPS16e_lwsp_op: | ||
1362 | reg = reg16to32[mips16inst.ri.rx]; | ||
1363 | break; | ||
1364 | |||
1365 | case MIPS16e_i8_op: | ||
1366 | if (mips16inst.i8.func != MIPS16e_swrasp_func) | ||
1367 | goto sigbus; | ||
1368 | reg = 29; /* GPRSP */ | ||
1369 | break; | ||
1370 | |||
1371 | default: | ||
1372 | reg = reg16to32[mips16inst.rri.ry]; | ||
1373 | break; | ||
1374 | } | ||
1375 | |||
1376 | switch (mips16inst.ri.opcode) { | ||
1377 | |||
1378 | case MIPS16e_lb_op: | ||
1379 | case MIPS16e_lbu_op: | ||
1380 | case MIPS16e_sb_op: | ||
1381 | goto sigbus; | ||
1382 | |||
1383 | case MIPS16e_lh_op: | ||
1384 | if (!access_ok(VERIFY_READ, addr, 2)) | ||
1385 | goto sigbus; | ||
1386 | |||
1387 | LoadHW(addr, value, res); | ||
1388 | if (res) | ||
1389 | goto fault; | ||
1390 | MIPS16e_compute_return_epc(regs, &oldinst); | ||
1391 | regs->regs[reg] = value; | ||
1392 | break; | ||
1393 | |||
1394 | case MIPS16e_lhu_op: | ||
1395 | if (!access_ok(VERIFY_READ, addr, 2)) | ||
1396 | goto sigbus; | ||
1397 | |||
1398 | LoadHWU(addr, value, res); | ||
1399 | if (res) | ||
1400 | goto fault; | ||
1401 | MIPS16e_compute_return_epc(regs, &oldinst); | ||
1402 | regs->regs[reg] = value; | ||
1403 | break; | ||
1404 | |||
1405 | case MIPS16e_lw_op: | ||
1406 | case MIPS16e_lwpc_op: | ||
1407 | case MIPS16e_lwsp_op: | ||
1408 | if (!access_ok(VERIFY_READ, addr, 4)) | ||
1409 | goto sigbus; | ||
1410 | |||
1411 | LoadW(addr, value, res); | ||
1412 | if (res) | ||
1413 | goto fault; | ||
1414 | MIPS16e_compute_return_epc(regs, &oldinst); | ||
1415 | regs->regs[reg] = value; | ||
1416 | break; | ||
1417 | |||
1418 | case MIPS16e_lwu_op: | ||
1419 | #ifdef CONFIG_64BIT | ||
1420 | /* | ||
1421 | * A 32-bit kernel might be running on a 64-bit processor. But | ||
1422 | * if we're on a 32-bit processor and an i-cache incoherency | ||
1423 | * or race makes us see a 64-bit instruction here the sdl/sdr | ||
1424 | * would blow up, so for now we don't handle unaligned 64-bit | ||
1425 | * instructions on 32-bit kernels. | ||
1426 | */ | ||
1427 | if (!access_ok(VERIFY_READ, addr, 4)) | ||
1428 | goto sigbus; | ||
1429 | |||
1430 | LoadWU(addr, value, res); | ||
1431 | if (res) | ||
1432 | goto fault; | ||
1433 | MIPS16e_compute_return_epc(regs, &oldinst); | ||
1434 | regs->regs[reg] = value; | ||
1435 | break; | ||
1436 | #endif /* CONFIG_64BIT */ | ||
1437 | |||
1438 | /* Cannot handle 64-bit instructions in 32-bit kernel */ | ||
1439 | goto sigill; | ||
1440 | |||
1441 | case MIPS16e_ld_op: | ||
1442 | loadDW: | ||
1443 | #ifdef CONFIG_64BIT | ||
1444 | /* | ||
1445 | * A 32-bit kernel might be running on a 64-bit processor. But | ||
1446 | * if we're on a 32-bit processor and an i-cache incoherency | ||
1447 | * or race makes us see a 64-bit instruction here the sdl/sdr | ||
1448 | * would blow up, so for now we don't handle unaligned 64-bit | ||
1449 | * instructions on 32-bit kernels. | ||
1450 | */ | ||
1451 | if (!access_ok(VERIFY_READ, addr, 8)) | ||
1452 | goto sigbus; | ||
1453 | |||
1454 | LoadDW(addr, value, res); | ||
1455 | if (res) | ||
1456 | goto fault; | ||
1457 | MIPS16e_compute_return_epc(regs, &oldinst); | ||
1458 | regs->regs[reg] = value; | ||
1459 | break; | ||
1460 | #endif /* CONFIG_64BIT */ | ||
1461 | |||
1462 | /* Cannot handle 64-bit instructions in 32-bit kernel */ | ||
1463 | goto sigill; | ||
1464 | |||
1465 | case MIPS16e_sh_op: | ||
1466 | if (!access_ok(VERIFY_WRITE, addr, 2)) | ||
1467 | goto sigbus; | ||
1468 | |||
1469 | MIPS16e_compute_return_epc(regs, &oldinst); | ||
1470 | value = regs->regs[reg]; | ||
1471 | StoreHW(addr, value, res); | ||
1472 | if (res) | ||
1473 | goto fault; | ||
1474 | break; | ||
1475 | |||
1476 | case MIPS16e_sw_op: | ||
1477 | case MIPS16e_swsp_op: | ||
1478 | case MIPS16e_i8_op: /* actually - MIPS16e_swrasp_func */ | ||
1479 | if (!access_ok(VERIFY_WRITE, addr, 4)) | ||
1480 | goto sigbus; | ||
1481 | |||
1482 | MIPS16e_compute_return_epc(regs, &oldinst); | ||
1483 | value = regs->regs[reg]; | ||
1484 | StoreW(addr, value, res); | ||
1485 | if (res) | ||
1486 | goto fault; | ||
1487 | break; | ||
1488 | |||
1489 | case MIPS16e_sd_op: | ||
1490 | writeDW: | ||
1491 | #ifdef CONFIG_64BIT | ||
1492 | /* | ||
1493 | * A 32-bit kernel might be running on a 64-bit processor. But | ||
1494 | * if we're on a 32-bit processor and an i-cache incoherency | ||
1495 | * or race makes us see a 64-bit instruction here the sdl/sdr | ||
1496 | * would blow up, so for now we don't handle unaligned 64-bit | ||
1497 | * instructions on 32-bit kernels. | ||
1498 | */ | ||
1499 | if (!access_ok(VERIFY_WRITE, addr, 8)) | ||
1500 | goto sigbus; | ||
1501 | |||
1502 | MIPS16e_compute_return_epc(regs, &oldinst); | ||
1503 | value = regs->regs[reg]; | ||
1504 | StoreDW(addr, value, res); | ||
1505 | if (res) | ||
1506 | goto fault; | ||
1507 | break; | ||
1508 | #endif /* CONFIG_64BIT */ | ||
1509 | |||
1510 | /* Cannot handle 64-bit instructions in 32-bit kernel */ | ||
1511 | goto sigill; | ||
1512 | |||
1513 | default: | ||
1514 | /* | ||
1515 | * Pheeee... We encountered an yet unknown instruction or | ||
1516 | * cache coherence problem. Die sucker, die ... | ||
1517 | */ | ||
1518 | goto sigill; | ||
1519 | } | ||
1520 | |||
1521 | #ifdef CONFIG_DEBUG_FS | ||
1522 | unaligned_instructions++; | ||
1523 | #endif | ||
1524 | |||
1525 | return; | ||
1526 | |||
1527 | fault: | ||
1528 | /* roll back jump/branch */ | ||
1529 | regs->cp0_epc = origpc; | ||
1530 | regs->regs[31] = orig31; | ||
1531 | /* Did we have an exception handler installed? */ | ||
1532 | if (fixup_exception(regs)) | ||
1533 | return; | ||
1534 | |||
1535 | die_if_kernel("Unhandled kernel unaligned access", regs); | ||
1536 | force_sig(SIGSEGV, current); | ||
1537 | |||
1538 | return; | ||
1539 | |||
1540 | sigbus: | ||
1541 | die_if_kernel("Unhandled kernel unaligned access", regs); | ||
1542 | force_sig(SIGBUS, current); | ||
1543 | |||
1544 | return; | ||
1545 | |||
1546 | sigill: | ||
1547 | die_if_kernel | ||
1548 | ("Unhandled kernel unaligned access or invalid instruction", regs); | ||
1549 | force_sig(SIGILL, current); | ||
1550 | } | ||
1307 | asmlinkage void do_ade(struct pt_regs *regs) | 1551 | asmlinkage void do_ade(struct pt_regs *regs) |
1308 | { | 1552 | { |
1309 | unsigned int __user *pc; | 1553 | unsigned int __user *pc; |
@@ -1351,6 +1595,17 @@ asmlinkage void do_ade(struct pt_regs *regs) | |||
1351 | return; | 1595 | return; |
1352 | } | 1596 | } |
1353 | 1597 | ||
1598 | if (cpu_has_mips16) { | ||
1599 | seg = get_fs(); | ||
1600 | if (!user_mode(regs)) | ||
1601 | set_fs(KERNEL_DS); | ||
1602 | emulate_load_store_MIPS16e(regs, | ||
1603 | (void __user *)regs->cp0_badvaddr); | ||
1604 | set_fs(seg); | ||
1605 | |||
1606 | return; | ||
1607 | } | ||
1608 | |||
1354 | goto sigbus; | 1609 | goto sigbus; |
1355 | } | 1610 | } |
1356 | 1611 | ||