5

I am trying to edit a file inside /proc/devices-tree to be exact, and i am unable to do so, I am getting:

"permission denied" or "Input/Output error".

I have tried all possible combinations of editors, chown, chmod and even sudo dd. I also know the exact memory location to write to 7000c400 in hex. I need to replace 4 bytes there, is there any method which may help me achieve this.

Edit: What i am trying to achieve by attempting this?

  • I have a Jetson-TK1 board, and an i2c bus is set to a default of 400kHz, but i want to run it at 100kHz. I think i can do that by changing device tree structure and recompilation, but recompilation is a lot bigger headache, as the kernel i am using is not a standard one (nvidia doesn't provide that).

    I had read somewhere that in Linux almost everything is in the form of a file. So looking for it, i found a file which contains 4 bytes which evaluates to 400000, I think changing this file would change the frequency.

  • Now the real problem is i was unable to change it (i think i am a decent enough user and as far as i understand, if there is something in memory and i have all kinds of passwords, i should be able to change it. That fact that i mess up something is not the question). I tried all possible methods known to me (as i have added in the question). So how do i do that.

  • What would you do? – Helio Mar 23 '15 at 21:27
  • I did not understand your query? – Rishabh Mar 24 '15 at 15:08
  • 1
    IIRC, /proc/device-tree is simply a read-only data structure used to enumerate information regarding system hardware, so I'm not quite sure what you mean by saying you want to "edit a file inside [it]." Also, was your kernel compiled with CONFIG_PROC_DEVICETREE=Y? If you want to change the speed of an i2c bus on your device, you might want to instead look at the appropriate sysfs control inode. – joshumax Mar 26 '15 at 19:02
  • 1
    Whatever sits in kernel space is not accessible from user programs, unless one would use some DMA techniques like editing /dev/mem. Not sure if mmap() could do the trick either, I believe it should. The more "elegant" way tough would be to edit the proc file directly from kernel space (i.e. from a kernel module) using the appropriate functions in proc_fs.h. I'll try to write a program to do that using one of these methods, hopefully I'll be able to work something out! – kos Mar 28 '15 at 18:47
  • 1
    @kos, you can prepare an linux/ioctl.h interface in the kernel. Then use fcntl.h from user space to communicate with the kernel module. See Talking to Device Files (writes and IOCTLs) – user.dz Mar 31 '15 at 9:17
3
+150

I looked into this mostly for fun and for learning (and hopefully for the rep!). I wish I could have some more time to play with ioctl (thanks to Sneetsher for the suggestion) and with what I've done so far in order to make a more elegant solution, but the bounty is about to expire and it's not likely that I can make everything in time, so I'm posting this solution "as it is" (at least for now).

Disclaimer:

I don't know which are the consequences of changing something into /proc/device-tree, so if you really know what you're doing, keep reading.

This particular implementation of this solution requires a running kernel > 3.10. It involves the compilation of a custom kernel module and the execution of a bash script to perform a sort of hot-switch between /proc/device-tree and a custom file device-tree_new.

Limits:

  1. Upon module removal, the custom /proc/device-tree it's removed! Another reason to read the disclaimer again.
  2. The custom /proc/device-tree's buffer has a limit of 65535 characters. Everything over the 65535 character is truncated. To adjust the buffer's size, change the following constant definition and variable declaration in the module's source code:

    1. #define MAX_BUFFER_SIZE 65535
    2. static unsigned int proc_buffer_length_v; (so that it can hold a number > 65535)

How it works:

The module itself:

  • deletes /proc/device-tree
  • creates a new blank /proc/device-tree with permissions 0666

The bash script itself:

  1. Loads the module
  2. Writes into /proc/device-tree the content of device-tree_new

This is the "Makefile" Makefile for the module (note that all the whitespaces at the beginning of each make line must be replaced with a TAB character):

obj-m += proc_module.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

This is the "proc_module.c" source file of the module:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

#define DEBUG 1
#define MAX_BUFFER_SIZE 65535

static struct proc_dir_entry* proc_dir_entry_p;
static struct file_operations file_operations_s;
static char* proc_buffer_p;
static unsigned int proc_buffer_length_v;
static unsigned short int read_flag;

int read_proc(struct file* file, char* buffer, size_t count, loff_t* offset) {
    if(DEBUG) printk(KERN_INFO "read_proc() called.\n");
    if(read_flag)
        read_flag = 0;
    else {
        read_flag = 1;
        return 0;
    }
    copy_to_user(buffer, proc_buffer_p, proc_buffer_length_v);
    if(DEBUG) printk(KERN_INFO "Ok. (count = %zu)\n", count);
    return proc_buffer_length_v;
}

int write_proc(struct file* file, char* buffer, size_t count, loff_t* offset) {
    size_t n;
    if(DEBUG) printk(KERN_INFO "write_proc() called.\n");
    if(count >= MAX_BUFFER_SIZE) {
        if(DEBUG) printk(KERN_INFO "write_proc(): Buffer exceeded!\n");
        n = MAX_BUFFER_SIZE;
    }
    else
        n = count;
    kfree(proc_buffer_p);
    if(DEBUG) printk(KERN_INFO "kfree() called.\n");
    if(!(proc_buffer_p = (char*)kmalloc(MAX_BUFFER_SIZE*sizeof(char), GFP_KERNEL))) {
        if(DEBUG) printk(KERN_INFO "kmalloc() ko.\n");
        return count;
    }
    if(DEBUG) printk(KERN_INFO "kmalloc() ok.\n");
    copy_from_user(proc_buffer_p, buffer, n);
    proc_buffer_length_v = n;
    if(DEBUG) printk(KERN_INFO "Ok. (count = %zu)\n", count);
    return count;
}

static int __init init_f(void) {
    if(DEBUG) printk(KERN_INFO "Module inserted.\n");
    remove_proc_entry("device-tree", NULL);
    if(!(proc_dir_entry_p = proc_create("device-tree", 0666, NULL, &file_operations_s))) {
        if(DEBUG) printk(KERN_INFO "Proc entry not created.\n");
        return -1;
    }
    if(DEBUG) printk(KERN_INFO "Proc entry created.\n");
    file_operations_s.read = read_proc;
    file_operations_s.write = write_proc;
    if(!(proc_buffer_p = (char*)kmalloc(1*sizeof(char), GFP_KERNEL))) {
        if(DEBUG) printk(KERN_INFO "kmalloc() ko.\n");
        return -1;
    }
    if(DEBUG) printk(KERN_INFO "kmalloc() ok.\n");
    proc_buffer_p[0] = '\0';
    proc_buffer_length_v = 0;
    read_flag = 1;
    if(DEBUG) printk(KERN_INFO "Ok.\n");
    return 0;
}

static void __exit exit_f(void) {
    kfree(proc_buffer_p);
    if(DEBUG) printk(KERN_INFO "kfree() called.\n");
    proc_remove(proc_dir_entry_p);
    if(DEBUG) printk(KERN_INFO "Proc entry removal requested.\n");
    if(DEBUG) printk(KERN_INFO "Module removed.\n");
}

module_init(init_f);
module_exit(exit_f);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("kos");
MODULE_DESCRIPTION("proc_module");

This is the "switch.sh" bash script:

#!/bin/bash

sudo rmmod proc_module.ko
sudo insmod proc_module.ko && cat device-tree_new > /proc/device-tree

Instructions:

  1. Open a Terminal with Ctrl+Alt+t
  2. Create a new folder: mkdir <folder_name>
  3. Change the current working directory to the new folder: cd <folder_name>
  4. Create the three files above using the exact same names enclosed between the double-quotes
  5. Create the custom device-tree file and name it device-tree_new
  6. Mark "switch.sh" as executable: chmod a+x switch.sh
  7. Compile the module: make (two warnings will be thrown by gcc)
  8. Launch the bash script: ./switch.sh
  9. cat /proc/device-tree to see the result
  • You are writing to a file name /proc/device-tree, but you don't modify the purpose of the original /proc/device-tree... You answer the question but don't resolve the problem. – Nimlar Apr 2 '15 at 8:01
  • @Nimlar It might be, as I said i don't know which role the device-tree file plays exactly, so can you elaborate on this? – kos Apr 2 '15 at 12:42
2

/proc/ is a pseudo file system: when you read/write on any /proc/file you don't access a real file or real memory, but you call some specific kernel function (depending of the file) that acts as a file. It returns data if you read the file, set data if you write to the file. And if there is no write function defined for a specific file, writing to the file won't change anything.

In this case the /proc/device-tree is a way to read the device tree provided to the running kernel while its boot. (no write enable)

Moreover, currently, the device tree is a read-only configuration, you cannot update it after the boot. And for your specific case, the values configuring your i2c are read and used when the i2c is probed ('installed'). If you want to reconfigure i2c, you need as said joshumax, to use the correct ioctl on the i2c device (in /dev/ where some specific "driver entry" are defined)

One other solution is to build a new device tree, configuring the I2C device as you want. And ask the kernel (check the bootloader you are using) to use the device tree you just compiled.

1

You’ll need rootly powers use sudo for that. Try this: you can use gdb (GNU Debugger) running as root to manipulate contents of memory. These may interest you:

http://sourceware.org/gdb/current/onlinedocs/gdb/

https://stackoverflow.com/questions/3305164/how-to-modify-memory-contents-using-gdb

  • 2
    malloc() is not useful. malloc() is used to allocate memory in userspace, while what the user wants is to write something already in kernel space. He'll rather have to make a clever use of mmap(). – kos Mar 27 '15 at 14:33
  • Yea, i think that is right. – j0h Mar 27 '15 at 14:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.