alignment.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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. static unsigned long se_user;
  18. static unsigned long se_sys;
  19. static unsigned long se_half;
  20. static unsigned long se_word;
  21. static unsigned long se_dword;
  22. static unsigned long se_multi;
  23. /* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not
  24. valid! */
  25. static int se_usermode = UM_WARN | UM_FIXUP;
  26. /* 0: no warning 1: print a warning message, disabled by default */
  27. static int se_kernmode_warn;
  28. void inc_unaligned_byte_access(void)
  29. {
  30. se_half++;
  31. }
  32. void inc_unaligned_word_access(void)
  33. {
  34. se_word++;
  35. }
  36. void inc_unaligned_dword_access(void)
  37. {
  38. se_dword++;
  39. }
  40. void inc_unaligned_multi_access(void)
  41. {
  42. se_multi++;
  43. }
  44. void inc_unaligned_user_access(void)
  45. {
  46. se_user++;
  47. }
  48. void inc_unaligned_kernel_access(void)
  49. {
  50. se_sys++;
  51. }
  52. unsigned int unaligned_user_action(void)
  53. {
  54. return se_usermode;
  55. }
  56. void unaligned_fixups_notify(struct task_struct *tsk, insn_size_t insn,
  57. struct pt_regs *regs)
  58. {
  59. if (user_mode(regs) && (se_usermode & UM_WARN) && printk_ratelimit())
  60. pr_notice("Fixing up unaligned userspace access "
  61. "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
  62. tsk->comm, task_pid_nr(tsk),
  63. (void *)instruction_pointer(regs), insn);
  64. else if (se_kernmode_warn && printk_ratelimit())
  65. pr_notice("Fixing up unaligned kernel access "
  66. "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
  67. tsk->comm, task_pid_nr(tsk),
  68. (void *)instruction_pointer(regs), insn);
  69. }
  70. static const char *se_usermode_action[] = {
  71. "ignored",
  72. "warn",
  73. "fixup",
  74. "fixup+warn",
  75. "signal",
  76. "signal+warn"
  77. };
  78. static int alignment_proc_show(struct seq_file *m, void *v)
  79. {
  80. seq_printf(m, "User:\t\t%lu\n", se_user);
  81. seq_printf(m, "System:\t\t%lu\n", se_sys);
  82. seq_printf(m, "Half:\t\t%lu\n", se_half);
  83. seq_printf(m, "Word:\t\t%lu\n", se_word);
  84. seq_printf(m, "DWord:\t\t%lu\n", se_dword);
  85. seq_printf(m, "Multi:\t\t%lu\n", se_multi);
  86. seq_printf(m, "User faults:\t%i (%s)\n", se_usermode,
  87. se_usermode_action[se_usermode]);
  88. seq_printf(m, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn,
  89. se_kernmode_warn ? "+warn" : "");
  90. return 0;
  91. }
  92. static int alignment_proc_open(struct inode *inode, struct file *file)
  93. {
  94. return single_open(file, alignment_proc_show, NULL);
  95. }
  96. static ssize_t alignment_proc_write(struct file *file,
  97. const char __user *buffer, size_t count, loff_t *pos)
  98. {
  99. int *data = PDE(file->f_path.dentry->d_inode)->data;
  100. char mode;
  101. if (count > 0) {
  102. if (get_user(mode, buffer))
  103. return -EFAULT;
  104. if (mode >= '0' && mode <= '5')
  105. *data = mode - '0';
  106. }
  107. return count;
  108. }
  109. static const struct file_operations alignment_proc_fops = {
  110. .owner = THIS_MODULE,
  111. .open = alignment_proc_open,
  112. .read = seq_read,
  113. .llseek = seq_lseek,
  114. .release = single_release,
  115. .write = alignment_proc_write,
  116. };
  117. /*
  118. * This needs to be done after sysctl_init, otherwise sys/ will be
  119. * overwritten. Actually, this shouldn't be in sys/ at all since
  120. * it isn't a sysctl, and it doesn't contain sysctl information.
  121. * We now locate it in /proc/cpu/alignment instead.
  122. */
  123. static int __init alignment_init(void)
  124. {
  125. struct proc_dir_entry *dir, *res;
  126. dir = proc_mkdir("cpu", NULL);
  127. if (!dir)
  128. return -ENOMEM;
  129. res = proc_create_data("alignment", S_IWUSR | S_IRUGO, dir,
  130. &alignment_proc_fops, &se_usermode);
  131. if (!res)
  132. return -ENOMEM;
  133. res = proc_create_data("kernel_alignment", S_IWUSR | S_IRUGO, dir,
  134. &alignment_proc_fops, &se_kernmode_warn);
  135. if (!res)
  136. return -ENOMEM;
  137. return 0;
  138. }
  139. fs_initcall(alignment_init);