extable.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/bitfield.h>
  3. #include <linux/extable.h>
  4. #include <linux/string.h>
  5. #include <linux/errno.h>
  6. #include <linux/panic.h>
  7. #include <asm/asm-extable.h>
  8. #include <asm/extable.h>
  9. const struct exception_table_entry *s390_search_extables(unsigned long addr)
  10. {
  11. const struct exception_table_entry *fixup;
  12. size_t num;
  13. fixup = search_exception_tables(addr);
  14. if (fixup)
  15. return fixup;
  16. num = __stop_amode31_ex_table - __start_amode31_ex_table;
  17. return search_extable(__start_amode31_ex_table, num, addr);
  18. }
  19. static bool ex_handler_fixup(const struct exception_table_entry *ex, struct pt_regs *regs)
  20. {
  21. regs->psw.addr = extable_fixup(ex);
  22. return true;
  23. }
  24. static bool ex_handler_ua_store(const struct exception_table_entry *ex, struct pt_regs *regs)
  25. {
  26. unsigned int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
  27. regs->gprs[reg_err] = -EFAULT;
  28. regs->psw.addr = extable_fixup(ex);
  29. return true;
  30. }
  31. static bool ex_handler_ua_load_mem(const struct exception_table_entry *ex, struct pt_regs *regs)
  32. {
  33. unsigned int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data);
  34. unsigned int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
  35. size_t len = FIELD_GET(EX_DATA_LEN, ex->data);
  36. regs->gprs[reg_err] = -EFAULT;
  37. memset((void *)regs->gprs[reg_addr], 0, len);
  38. regs->psw.addr = extable_fixup(ex);
  39. return true;
  40. }
  41. static bool ex_handler_ua_load_reg(const struct exception_table_entry *ex,
  42. bool pair, struct pt_regs *regs)
  43. {
  44. unsigned int reg_zero = FIELD_GET(EX_DATA_REG_ADDR, ex->data);
  45. unsigned int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
  46. regs->gprs[reg_err] = -EFAULT;
  47. regs->gprs[reg_zero] = 0;
  48. if (pair)
  49. regs->gprs[reg_zero + 1] = 0;
  50. regs->psw.addr = extable_fixup(ex);
  51. return true;
  52. }
  53. static bool ex_handler_zeropad(const struct exception_table_entry *ex, struct pt_regs *regs)
  54. {
  55. unsigned int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data);
  56. unsigned int reg_data = FIELD_GET(EX_DATA_REG_ERR, ex->data);
  57. unsigned long data, addr, offset;
  58. addr = regs->gprs[reg_addr];
  59. offset = addr & (sizeof(unsigned long) - 1);
  60. addr &= ~(sizeof(unsigned long) - 1);
  61. data = *(unsigned long *)addr;
  62. data <<= BITS_PER_BYTE * offset;
  63. regs->gprs[reg_data] = data;
  64. regs->psw.addr = extable_fixup(ex);
  65. return true;
  66. }
  67. static bool ex_handler_fpc(const struct exception_table_entry *ex, struct pt_regs *regs)
  68. {
  69. asm volatile("sfpc %[val]\n" : : [val] "d" (0));
  70. regs->psw.addr = extable_fixup(ex);
  71. return true;
  72. }
  73. bool fixup_exception(struct pt_regs *regs)
  74. {
  75. const struct exception_table_entry *ex;
  76. ex = s390_search_extables(instruction_pointer(regs));
  77. if (!ex)
  78. return false;
  79. switch (ex->type) {
  80. case EX_TYPE_FIXUP:
  81. return ex_handler_fixup(ex, regs);
  82. case EX_TYPE_BPF:
  83. return ex_handler_bpf(ex, regs);
  84. case EX_TYPE_UA_STORE:
  85. return ex_handler_ua_store(ex, regs);
  86. case EX_TYPE_UA_LOAD_MEM:
  87. return ex_handler_ua_load_mem(ex, regs);
  88. case EX_TYPE_UA_LOAD_REG:
  89. return ex_handler_ua_load_reg(ex, false, regs);
  90. case EX_TYPE_UA_LOAD_REGPAIR:
  91. return ex_handler_ua_load_reg(ex, true, regs);
  92. case EX_TYPE_ZEROPAD:
  93. return ex_handler_zeropad(ex, regs);
  94. case EX_TYPE_FPC:
  95. return ex_handler_fpc(ex, regs);
  96. }
  97. panic("invalid exception table entry");
  98. }