I have a question where we need to implement internal working of open system call in gemOS.The question is as follows in the main function following
int create_fd = open(filename, O_CREAT|O_RDWR, O_READ|O_WRITE);
In Visual Studio Code when I checked definition of open using ctrl + enter, it lead me to following function. Lets suppose that Open system call has O_CREAT flag present, then accordingly _syscall3 will be called.
int open(char * filename, int flags, ...){
va_list ap;
long mode;
va_start(ap, flags);
mode = va_arg(ap, long);
va_end(ap);
if((flags & O_CREAT) == O_CREAT){
return _syscall3(SYSCALL_OPEN, (u64)filename, flags, mode);
}
else{
return _syscall2(SYSCALL_OPEN, (u64)filename, flags);
}
}
When I checked definition of _syscall3 it gave me following code.I was not able to understand what is written inside asm volatile command. So can anyone explain me what is happening.
static long _syscall3(int syscall_num, u64 arg1, u64 arg2, u64 arg3){
asm volatile (
"int $0x80;"
"leaveq;"
"retq;"
:::"memory"
);
return 0; /*gcc shutup!*/
}
Also when I tried printing something before asm volatile line, the function kinda stop executing. By the way the function that we need to implement for this call is following.
extern int do_regular_file_open(struct exec_context *ctx, char* filename, u64 flags, u64 mode){
/**
* TODO Implementation of file open,
* You should be creating file(use the alloc_file function to creat file),
* To create or Get inode use File system function calls,
* Handle mode and flags
* Validate file existence, Max File count is 16, Max Size is 4KB, etc
* Incase of Error return valid Error code
* */
return 100;
}
I was trying to map Open function written in main file to this do_regular_file_open, but due to asm volatile I wasn't able to understand whats happening.
Related
I am not actually sure where the best place to post this is as it is a combination of newlib code, FreeRTOS and a custom implementation. Application is embedded ARM using GCC (arm-eabi...), newlib from standard GCC for ARM installation, FreeRTOS on embedded target (STM32).
The root issue is that calling fprint(stderr, "error\n") fails the first time it is called. Calling printf before solves the issue.
Fails
int err = fprint(stderr, "error\n"); // err = -1, failed!
OK
int err = printf("hi\n"); // err = 3, OK
err = fprint(stderr, "error\n"); // err = 6, OK
I have found the cause of this issue, its a bit long winded to explain, but it comes down to threads reent structure being not fully initialised until the first call of a std out function that implicitly uses stdout, stderr or stdin. e.g. printf, puts etc.
I will try explain what is going on:
When a thread is created, FreeRTOS initiallises the threads reent struct using the default initialiser. This initialises the std file pointers to point to the FILE structures in the reent struct itself.
FreeRTOS tasks.c initialises the tasks reent struct:
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
/* Initialise this task's Newlib reent structure. */
_REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
}
#endif
_REENT_INIT_PTR sets the std file pointers in the reent struct to the file descriptors in the reent struct itself (https://github.com/eblot/newlib/blob/master/newlib/libc/include/sys/reent.h):
#define _REENT_INIT_PTR(var) \
{ memset((var), 0, sizeof(*(var))); \
(var)->_stdin = &(var)->__sf[0]; \
(var)->_stdout = &(var)->__sf[1]; \
(var)->_stderr = &(var)->__sf[2]; \
The file descriptors in the reent struct are by default zeroed (first line memset) so invalid.
Calling printf causes a call to initialise the reent struct. This is done by calling __sinit (via _REENT_SMALL_CHECK_INIT) if the structure has not been initialised before (https://github.com/eblot/newlib/blob/master/newlib/libc/stdio/printf.c).
int
_DEFUN(printf, (fmt),
const char *fmt _DOTS)
{
int ret;
va_list ap;
struct _reent *ptr = _REENT;
_REENT_SMALL_CHECK_INIT (ptr);
va_start (ap, fmt);
ret = _vfprintf_r (ptr, _stdout_r (ptr), fmt, ap);
va_end (ap);
return ret;
}
(https://github.com/eblot/newlib/blob/master/newlib/libc/include/sys/reent.h)
# define _REENT_SMALL_CHECK_INIT(ptr) \
do \
{ \
if ((ptr) && !(ptr)->__sdidinit) \
__sinit (ptr); \
} \
while (0)
__sinit does a whole lot of work including initialising the global reeent structure (if it has not been initialised), and copying the global reent structures stdio file pointers to the tasks local stdio file points, thereby making them valid. __sinit definiton is in https://github.com/eblot/newlib/blob/master/newlib/libc/stdio/findfp.c.
Note that fprintf does not call __sinit, so in this case, the stderr file pointer is used uninitialised if fprintf is called before printf (https://github.com/eblot/newlib/blob/master/newlib/libc/stdio/fprintf.c).
int
_DEFUN(fprintf, (fp, fmt),
FILE *fp _AND
const char *fmt _DOTS)
{
int ret;
va_list ap;
va_start (ap, fmt);
ret = _vfprintf_r (_REENT, fp, fmt, ap);
va_end (ap);
return ret;
}
So, while I am not claiming that anything is broken, I am not sure how the tasks local reent structure is meant to get initialised before any calls to fprintf. A simple printf at the start of the thread would resolve this. But it would mean I need to do that at the start of every task that might use fprintf. By default assert calls fprintf, so I would need to have a printf at the start of any thread that might use assert (actually how this issue was found).
I am interested to hear any feedback or advice on this one. I already have a workaround for this application (custom assert function), but I would like to understand and learn a bit more about what is going on here.
I am developing a Linux kernel module which read my embedded board button code and use it in order to turn on/off the board led.
When I push button, the module is sending correctly the button code, which is 260, to user-space.
Now, from user-space, I am using the write function in order to re-send this code kernel module :
write(fd, buf_wr, strlen(buf_wr));
Where fd is the file descriptor of my module, and buf_wr is the buffer which will be written to the file.
In kernel space, I am using the write_pid function in this way :
static ssize_t write_pid(struct file *pfile, const char __user *buffer,
size_t length, loff_t *offset)
{
char cod_buf[12];
printk("WE ARE IN WRITE_PID FUNCTION\n");
copy_from_user(cod_buf, buffer, length);
sscanf(cod_buf, "%i", &lcode);
printk("lcode = %i\n", lcode);
return 0;
}
I defined lcode in my kernel module as a global variable :
int lcode = 0;
and I can see that it is receiving the correct button code which is 260.
Now, I created a thread in the module, I want this thread to run some instructions basing on the button code :
int write_in_thread(void *data) {
printk("Under write_in_thread, lcode = %i\n", lcode);
switch (lcode) {
case 260:
//instructions....
//instructions....
//instructions....
break;
default :
//instructions....
break;
}
return 0;
}
The problem here, I can see that, Under write_in_thread, lcode = 0. It's not 260. So the case statement is running the default one which I don't need here.
How can I fix the lcode variable in order to keep it's value in the thread function?
Thank you!
I am using libnfs for C. I am calling nfs_read and it takes the size of bytes to read as a uint64_t variable. I have the size defined as a macro (#define 100). I mostly get a segfault (or sometimes some other error based on what value I choose, but always the same error for the specific value)for any size value greater than 24. I also tried changing #define to global uint64_t. I first had it in a header file and also moved it from header file to c file. But the result is always the same (if size is greater than 24), segfault.
But when I pass the value directly (as hard coded value) to the function nfs_read I do not get segfault, for any value of size (<24 or >24).
I have done fair number of projects in C before and have never faced such an error. Any idea what could be happening here. Thanks.
typedef struct {
int is_nfs;
int fd;
struct nfs_context *nfs;
struct nfsfh *fh;
}nfs_fd_t;
#define COUNT 100
// The open function is same as in the linked example except for I added nfs_dd_t struct to store nfs and fh
int dd_open(const char *path, int flags, mode_t mode, nfs_fd_t *nfs_fd);
ssize_t read_wrapper(nfs_dd_t *nfs_fd)
{
char * buf = malloc(COUNT);
int ret = dd_read(nfs_fd, buf, COUNT);
// follow up logic
}
ssize_t dd_read(nfs_fd_t *nfs_fd, void *buf, uint64_t count)
{
int ret;
if ((ret = nfs_read((*nfs_fd).nfs, (*nfs_fd).fh, count, (char *)buf)) < 0) {
errno = -ret;
return -1;
}
return ret;
}
nfs_dd_t is a struct containing nfs and fh. I am basically following this example only slightly modified for my need.
I'm supposed to change a configuration parameter of the kernel by using a kernel module. The kernel module should create a proc file and then I should be able to change the parameter by using the cat command, e.g. cat "foobar" > /proc/prompt is supposed to set the parameter to "foobar", where prompt is the name of the proc file that was created in the module.
Furthermore I should be able to initialize the parameter by passing it as an argument when calling the module.
These two articles were basically the only relevant sources that I have found:
http://www.tldp.org/LDP/lkmpg/2.6/html/x769.html for writing to a proc file and http://www.tldp.org/LDP/lkmpg/2.6/html/x323.html for initializing the parameter from the command line.
Now I have a couple of questions, first of all this is the module thus far:
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include "sar_main.h"
#define PROCFS_NAME "sarlkm"
char procfs_buffer[PROCFS_MAX_SIZE];
static unsigned long procfs_buffer_size = 0
struct proc_dir_entry *proc_file_entry;
int procfile_read(char *buffer, char **buffer_location, off_t offset, int buffer_length, int *eof, void *data){
int ret;
printk(KERN_INFO "procfile_read (/proc/%s) aufgerufen \n", PROCFS_NAME);
if (offset > 0){
ret = 0;
}
else{
memcpy(buffer, procfs_buffer, procfs_buffer_size);
ret = procfs_buffer_size;
}
return ret;
}
int procfile_write(struct file *file, const char *buffer, unsigned long count, void *data){
procfs_buffer_size = count;
if (procfs_buffer_size > PROCFS_MAX_SIZE){
procfs_buffer_size = PROCFS_MAX_SIZE;
}
if ( copy_from_user(procfs_buffer, buffer, procfs_buffer)){
return -EFAULT;
}
return procfs_buffer_size;
}
static int __init sar_init(void)
{
prompt_proc = create_proc_entry(PROCFS_NAME, 0644, NULL);
if (prompt_proc = NULL){
remove_proc_entry(PROCFS_NAME, &proc_root);
printk(KERN_ALERT "Error: Konnte proc file nicht kreieren")
return -ENOMEM;
}
prompt_proc->read_proc = procfile_read;
prompt_proc->write_proc = procfile_write;
printk(KERN_INFO "proc/%s wurde erfolgreich kreiert", PROCFS_NAME);
return 0;
}
static void __exit sar_cleanup(void)
{
remove_proc_entry(PROCFS_NAME, &proc_root);
printk(KERN_INFO "proc/%s gelöscht", PROCFS_NAME);
}
module_init(sar_init);
module_exit(sar_cleanup);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
I think I should say that I don't really understand why the read and write functions are supposed to work when using the cat command.
My main question is where exactly is the configuration parameter stored in the proc file? If I would write "foobar" to the proc file using cat and then use cat proc/prompt to read the parameter, how does the read function actually get the new value of the parameter, i.e. where is "foobar" stored in the procfile?
If I would try to initialize the parameter using a command line argument I would have to use a global variable in which to store the value of the parameter, but then how could I use that global variable in the read function, so that cat proc/prompt actually gives out the value that was given to the module from the command line?
The cat command internally calls the read() system call to read data from a file (see man strace).
read() passes the arguments to the VFS and the VFS finally calls your custom procfile_read() routine with the passed arguments (and some additional ones passed by the VFS code). If you want to know more about this, look at the fs directory in kernel sources, especially file read_write.c.
Your particular reading function copies, if some conditions are met, the parameter value (which is stored in procfs_buffer to answer one of your questions) into the user-supplied buffer allocated by cat, which is called buffer in your particular code. It is the same one as passed by the read() system call like in:
read(proc_fd, userspace_buf, 10); /* userspace_buf is buffer! */
Error checking omitted for clearness's sake.
To pass the value to the proc file you have two options:
Use module_param() and write it to your buffer; can only be done once because the module is only loadable once (or unload/reload it every time you want to change the parameter but that sounds inconvenient)
Invoke write() from userspace (like in cat) and modify the buffer as often as you want to (this is currently used by your code)
BTW, I really think your reading function should check the pointer to the user data, i.e. use copy_to_user(), not memcpy().
For further information, read Linux Device Drivers. There's only an old edition available at the moment but an updated one is being written.
you can treat xxx_write or xxx_read in driver just as a interface implement,
when you call write or read in user space,
the kernel will invoke xxx_write or xxx_read in kernel space.
so you need to store it yourself when write call,
and fetch them back when read call,
in xxx_write xxx_read
I'd like to share a variable between kernel and user space and I've found that it's possible with procfs.
The kernel module must act in certain way if given value is set. The user space program is responsible for changing this value, but the kernel module must read it when necessary.
I know that I must create the /proc file in the kernel module.
My question is, how to read the file from the kernel module?
Source : linux.die.net/lkmpg/x769.html
/**
* This function is called with the /proc file is written
*
*/
int procfile_write(struct file *file, const char *buffer, unsigned long count,
void *data)
{
/* get buffer size */
procfs_buffer_size = count;
if (procfs_buffer_size > PROCFS_MAX_SIZE ) {
procfs_buffer_size = PROCFS_MAX_SIZE;
}
/* write data to the buffer */
if ( copy_from_user(procfs_buffer, buffer, procfs_buffer_size) ) {
return -EFAULT;
}
return procfs_buffer_size;
}
To clarify, in Your module whenever user writes to Your file in procfs, this example shows how to handle such write.
In kernel >= 3.10 proc_write is moved to structure file_operations where declaration of write is different, so in newest your solution won't work.
You can implement typical file_operations.write(struct file *, const char __user *, size_t, loff_t *) and reference this to:
struct proc_dir_entry your_proc_dir_entry{
.proc_fops = &your_fops,
}