/* * This is a simple character device driver to illustrate the support for debug of Linux kernel modules * * Copyright (C) ARM Limited, 2010. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include #define MODEX_MAJOR 42 #define MODEX_MINOR 0 #define DEVICE_NAME "modex" static int modex_open(struct inode *inode, struct file *file); static int modex_release(struct inode *inode, struct file *file); static ssize_t modex_read (struct file *file, char *buffer, size_t length, loff_t * offset); static ssize_t modex_write (struct file *file, const char *buffer, size_t length, loff_t * offset); static int __init modex_init(void); static void __exit modex_exit(void); static volatile int test_mode /* = 0 */; /* can be set from debugger */ struct file_operations modex_fops = { .owner = THIS_MODULE, .open = modex_open, .release = modex_release, .read = modex_read, .write = modex_write, }; static struct device *modex_cdev; static struct class *modex_class; static int modex_open(struct inode *inode, struct file *file) { return 0 ; } static int modex_release(struct inode *inode, struct file *file) { return 0; } static ssize_t modex_read(struct file *file, char *buffer, size_t length, loff_t *offset) { char msg_buffer[920]; char* msg = msg_buffer; int bytes_written; const char* ctype[] = { "No cache", "I$ only", "D$ only", "I & D $", "Unified", "RESERVED - should not see this", "RESERVED - should not see this", "RESERVED - should not see this" }; int msg_len; volatile unsigned int actlr, midr, clidr, ccsidr_l1d, ccsidr_l2u; unsigned int l1d = 0; unsigned int l2u = 2; #ifdef __arm__ asm("mrc p15, 0, %0, c1, c0, 1" : "=r" (actlr)); //read actlr asm("mrc p15, 0, %0, c0, c0, 0" : "=r" (midr)); //read midr asm("mrc p15, 1, %0, c0, c0, 1" : "=r" (clidr)); asm volatile("mcr p15, 2, %0, c0, c0, 0\nisb" :: "r" (l1d)); //set csselr (cache size selection register) asm volatile("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr_l1d)); asm volatile("mcr p15, 2, %0, c0, c0, 0\nisb" :: "r" (l2u)); //set csselr (cache size selection register) asm volatile("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr_l2u)); #elif defined __aarch64__ asm("mrs %0, ACTLR_EL1" : "=r" (actlr)); asm("mrs %0, MIDR_EL1" : "=r" (midr)); asm("mrs %0, CLIDR_EL1" : "=r" (clidr)); asm volatile("msr CSSELR_EL1, %0\nisb" :: "r" (l1d)); asm volatile("mrs %0, CCSIDR_EL1" : "=r" (ccsidr_l1d)); asm volatile("msr CSSELR_EL1, %0\nisb" :: "r" (l2u)); asm volatile("mrs %0, CCSIDR_EL1" : "=r" (ccsidr_l2u)); #else #error "Non-ARM compiler" #endif bytes_written = snprintf(msg, sizeof msg_buffer, "ACTLR: %#.8x\n" "MIDR: %#.8x\n" "CLIDR: %#.8x\n" " LoUU: %d\n" " LoC: %d\n" " LoUIS: %d\n" " L7: %s\n" " L6: %s\n" " L5: %s\n" " L4: %s\n" " L3: %s\n" " L2: %s\n" " L1: %s\n" , actlr, midr, clidr, (clidr >> 27) & 0x7, (clidr >> 24) & 0x7, (clidr >> 21) & 0x7, ctype[(clidr >> 18) & 0x7], ctype[(clidr >> 15) & 0x7], ctype[(clidr >> 12) & 0x7], ctype[(clidr >> 9) & 0x7], ctype[(clidr >> 6) & 0x7], ctype[(clidr >> 3) & 0x7], ctype[(clidr >> 0) & 0x7]); if((clidr >> 0) & 0x7) { //there is some cache bytes_written += snprintf(msg + bytes_written, sizeof msg_buffer - bytes_written, "Following data only reliable if the named cache exists!\n"); } if((clidr >> 3) & 0x7) { bytes_written += snprintf(msg + bytes_written, sizeof msg_buffer - bytes_written, "CCSIDR L2U$: %#.8x\n" " Sets: %d\n" " Assoc: %d\n" " Line Size: 2^%d words\n", ccsidr_l2u, ((ccsidr_l2u >> 13) & 0x7fff) + 1, ((ccsidr_l2u >> 3) & 0x03ff) + 1, ((ccsidr_l2u >> 0) & 0x0007) + 2); } if((clidr >> 0) & 0x7) { bytes_written += snprintf(msg + bytes_written, sizeof msg_buffer - bytes_written, "CCSIDR L1D$: %#.8x\n" " Sets: %d\n" " Assoc: %d\n" " Line Size: 2^%d words\n", ccsidr_l1d, ((ccsidr_l1d >> 13) & 0x7fff) + 1, ((ccsidr_l1d >> 3) & 0x03ff) + 1, ((ccsidr_l1d >> 0) & 0x0007) + 2); } msg_len = strlen(msg); if (*offset >= msg_len) return 0; /* EOF */ if (*offset + length >= msg_len) length = msg_len - *offset; if ( copy_to_user(buffer, msg + *offset, length) ) return -EFAULT; *offset += length; return length; } static ssize_t modex_write(struct file *file, const char *buffer, size_t length, loff_t *offset) { char x; if ( copy_from_user(&x, buffer, 1) ) // only interested in the first byte passed in return -EFAULT; printk(KERN_INFO "modex received value: '%c' %d\n", x, x); switch (x) { case 'A': if (test_mode) { printk(KERN_INFO "modex test mode about to abort\n"); /* cause an abort */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" x = *(int*)test_mode; #pragma GCC diagnostic pop printk(KERN_INFO "modex didn't expect to get here! '%c' %d\n", x, x); } break; case 'P': if (test_mode) { panic("panic from modex test mode"); } break; case '+': test_mode = 1; printk(KERN_INFO "modex test mode enabled\n"); break; case '-': test_mode = 0; printk(KERN_INFO "modex test mode disabled\n"); break; default: break; } return length; } static int __init modex_init(void) { int ret; /* Register character device driver */ ret = register_chrdev(MODEX_MAJOR, DEVICE_NAME, &modex_fops); if (ret < 0) { printk(KERN_ERR "modex could not initialize\n"); return ret; } /* Create a class /sys/class/modex/modex, then create a device node /dev/modex */ modex_class = class_create(THIS_MODULE, DEVICE_NAME); modex_cdev = device_create(modex_class, NULL, MKDEV (MODEX_MAJOR, MODEX_MINOR), NULL, DEVICE_NAME); printk(KERN_INFO "modex initialized\n"); return 0; } static void __exit modex_exit(void) { device_destroy(modex_class, MKDEV (MODEX_MAJOR, MODEX_MINOR)); class_destroy(modex_class); unregister_chrdev(MODEX_MAJOR, DEVICE_NAME); printk(KERN_INFO "modex exited\n"); } module_init(modex_init); module_exit(modex_exit); MODULE_AUTHOR("ARM"); MODULE_VERSION("1.01"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Example module");