alignment.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /*
  2. * Alignment access counters and corresponding user-space interfaces.
  3. *
  4. * Copyright (C) 2009 ST Microelectronics
  5. * Copyright (C) 2009 - 2010 Paul Mundt
  6. *
  7. * This file is subject to the terms and conditions of the GNU General Public
  8. * License. See the file "COPYING" in the main directory of this archive
  9. * for more details.
  10. */
  11. #include <linux/module.h>
  12. #include <linux/kernel.h>
  13. #include <linux/seq_file.h>
  14. #include <linux/proc_fs.h>
  15. #include <linux/uaccess.h>
  16. #include <asm/alignment.h>
  17. #include <asm/processor.h>
  18. static unsigned long se_user;
  19. static unsigned long se_sys;
  20. static unsigned long se_half;
  21. static unsigned long se_word;
  22. static unsigned long se_dword;
  23. static unsigned long se_multi;
  24. /* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not
  25. valid! */
  26. static int se_usermode = UM_WARN | UM_FIXUP;
  27. /* 0: no warning 1: print a warning message, disabled by default */
  28. static int se_kernmode_warn;
  29. core_param(alignment, se_usermode, int, 0600);
  30. void inc_unaligned_byte_access(void)
  31. {
  32. se_half++;
  33. }
  34. void inc_unaligned_word_access(void)
  35. {
  36. se_word++;
  37. }
  38. void inc_unaligned_dword_access(void)
  39. {
  40. se_dword++;
  41. }
  42. void inc_unaligned_multi_access(void)
  43. {
  44. se_multi++;
  45. }
  46. void inc_unaligned_user_access(void)
  47. {
  48. se_user++;
  49. }
  50. void inc_unaligned_kernel_access(void)
  51. {
  52. se_sys++;
  53. }
  54. /*
  55. * This defaults to the global policy which can be set from the command
  56. * line, while processes can overload their preferences via prctl().
  57. */
  58. unsigned int unaligned_user_action(void)
  59. {
  60. unsigned int action = se_usermode;
  61. if (current->thread.flags & SH_THREAD_UAC_SIGBUS) {
  62. action &= ~UM_FIXUP;
  63. action |= UM_SIGNAL;
  64. }
  65. if (current->thread.flags & SH_THREAD_UAC_NOPRINT)
  66. action &= ~UM_WARN;
  67. return action;
  68. }
  69. int get_unalign_ctl(struct task_struct *tsk, unsigned long addr)
  70. {
  71. return put_user(tsk->thread.flags & SH_THREAD_UAC_MASK,
  72. (unsigned int __user *)addr);
  73. }
  74. int set_unalign_ctl(struct task_struct *tsk, unsigned int val)
  75. {
  76. tsk->thread.flags = (tsk->thread.flags & ~SH_THREAD_UAC_MASK) |
  77. (val & SH_THREAD_UAC_MASK);
  78. return 0;
  79. }
  80. void unaligned_fixups_notify(struct task_struct *tsk, insn_size_t insn,
  81. struct pt_regs *regs)
  82. {
  83. if (user_mode(regs) && (se_usermode & UM_WARN) && printk_ratelimit())
  84. pr_notice("Fixing up unaligned userspace access "
  85. "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
  86. tsk->comm, task_pid_nr(tsk),
  87. (void *)instruction_pointer(regs), insn);
  88. else if (se_kernmode_warn && printk_ratelimit())
  89. pr_notice("Fixing up unaligned kernel access "
  90. "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
  91. tsk->comm, task_pid_nr(tsk),
  92. (void *)instruction_pointer(regs), insn);
  93. }
  94. static const char *se_usermode_action[] = {
  95. "ignored",
  96. "warn",
  97. "fixup",
  98. "fixup+warn",
  99. "signal",
  100. "signal+warn"
  101. };
  102. static int alignment_proc_show(struct seq_file *m, void *v)
  103. {
  104. seq_printf(m, "User:\t\t%lu\n", se_user);
  105. seq_printf(m, "System:\t\t%lu\n", se_sys);
  106. seq_printf(m, "Half:\t\t%lu\n", se_half);
  107. seq_printf(m, "Word:\t\t%lu\n", se_word);
  108. seq_printf(m, "DWord:\t\t%lu\n", se_dword);
  109. seq_printf(m, "Multi:\t\t%lu\n", se_multi);
  110. seq_printf(m, "User faults:\t%i (%s)\n", se_usermode,
  111. se_usermode_action[se_usermode]);
  112. seq_printf(m, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn,
  113. se_kernmode_warn ? "+warn" : "");
  114. return 0;
  115. }
  116. static int alignment_proc_open(struct inode *inode, struct file *file)
  117. {
  118. return single_open(file, alignment_proc_show, NULL);
  119. }
  120. static ssize_t alignment_proc_write(struct file *file,
  121. const char __user *buffer, size_t count, loff_t *pos)
  122. {
  123. int *data = PDE(file->f_path.dentry->d_inode)->data;
  124. char mode;
  125. if (count > 0) {
  126. if (get_user(mode, buffer))
  127. return -EFAULT;
  128. if (mode >= '0' && mode <= '5')
  129. *data = mode - '0';
  130. }
  131. return count;
  132. }
  133. static const struct file_operations alignment_proc_fops = {
  134. .owner = THIS_MODULE,
  135. .open = alignment_proc_open,
  136. .read = seq_read,
  137. .llseek = seq_lseek,
  138. .release = single_release,
  139. .write = alignment_proc_write,
  140. };
  141. /*
  142. * This needs to be done after sysctl_init, otherwise sys/ will be
  143. * overwritten. Actually, this shouldn't be in sys/ at all since
  144. * it isn't a sysctl, and it doesn't contain sysctl information.
  145. * We now locate it in /proc/cpu/alignment instead.
  146. */
  147. static int __init alignment_init(void)
  148. {
  149. struct proc_dir_entry *dir, *res;
  150. dir = proc_mkdir("cpu", NULL);
  151. if (!dir)
  152. return -ENOMEM;
  153. res = proc_create_data("alignment", S_IWUSR | S_IRUGO, dir,
  154. &alignment_proc_fops, &se_usermode);
  155. if (!res)
  156. return -ENOMEM;
  157. res = proc_create_data("kernel_alignment", S_IWUSR | S_IRUGO, dir,
  158. &alignment_proc_fops, &se_kernmode_warn);
  159. if (!res)
  160. return -ENOMEM;
  161. return 0;
  162. }
  163. fs_initcall(alignment_init);