Pass struct to xv6 system call - c

I'm aware that we are not able to pass parameters to xv6 system call directly and we are forced to use it's built in methods.
But all examples and questions in this site is about how to send integer to system call. Which it's answer is using argint() method.
But my question is, is there anyway to pass "struct" to a xv6 system call? Are there any bulit-in methods for this purpose too?
If there is, could you please say a simple example?

Passing a struct through system call is possible.
While one can't pass a struct itself as a system call parameter, passing a pointer to it is possible and will allow using it as both an input or output parameter.
Allowing to use as argument the data itself and not a pointer to it will damage the requirement of the system calls mechanism- as passing data must be implemented in a generic way to allow all data types to (as well as future structs) be used.
Let's have a look on an existing implementation of the system call fstat.
int fstat(int fd, struct stat *st);
fstat requires a file descriptor number as an input and outputs a matching stats information using struct stat.
struct stat {
short type; // Type of file
int dev; // File system's disk device
uint ino; // Inode number
short nlink; // Number of links to file
uint size; // Size of file in bytes
};
Although fstat uses a struct pointer as an output parameter, using it as an input will be similar.
The function sys_fstat in kernel code starts the implementation of fstat system call (XV6's convention is to handle parameter fetching from user space by sys_* functions).
int sys_fstat(void)
{
struct file *f;
struct stat *st;
if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0)
return -1;
return filestat(f, st);
}
This function first gets a corresponding struct file to the file descriptor number received by the first fstat function argument (using argfd). Then, fetches the struct stat pointer received by the second fstat function argument using argptr and saves the given pointer in a local (function scope) pointer variable.
At this point, all arguments were fetched and can be used by the kernel implementation.
Note: Although the struct stat pointer is a user-space pointer (located on the lower half of the virtual space), it is safe for the kernel to use it here because when the kernel is serving a process' system call, it uses the process' own paging table.

Although the above answer is correct but i prefered to write my own solutions to make it more usable for other viwers.
i used argptr to pass a pointer-to-struct to my system call.
in sysproc.c:
int sys_counts (void){
struct countTable *ct;
argptr (0 , (void*)&ct ,sizeof(*ct));
return counts(ct);
}
in proc.c:
int counts (struct countTable *ct){
for (int i=0 ; i<22 ; i++){
(ct->system_calls)[i] = count_calls[i] ;
}
return 22;
}
and finally in my user-space-program:
int main (){
struct countTable *ct = malloc (sizeof (struct countTable));
// call system call
counts(ct);
exit();
}

Although one of the answers is acceptable I wrote my answer that is clear and complete.
Note that passing an argument to system-call directly is impossible. we will use argptr to do that.
In userspace, we define a struct that we want to work with. in a user-level file like test.c
#include "types.h"
#include "stat.h"
#include "user.h"
struct Data
{
...
int id; // some fields
...
};
int main(int argc, char *argv[])
{
struct Data *data = malloc(sizeof(struct Data));
// call the systemcall
doSomeWork((void *)data);
exit();
}
In sysproc.c we define system-call and use argptr to get arguments:
int sys_doSomeWork(void){
struct Data *data;
argptr(0, (void *)&data, sizeof(*data));
return doSomeWork((void *)data);
}
and in proc.c we can write the functionality of system-call:
int doSomeWork(void *data){
// cast to (struct Data *)
struct Data *my_data = (struct Data *)data;
...
// work with my_data
...
return 0;
}
and to make Data struct accessible inside sysproc.c and proc.c we define Data struct inside defs.h:
struct Data
{
...
int id; // some fields
...
};

Related

how to get map fd and max extries if I have bpf_map object by using bpf_object__find_map_by_name(bpf_object,"hash_map"); ebpf

So struct bpf_map member definition of fd and max_entries (which I need) are in source file (libbpf.c) and libbpf.h contains just declaration of ebpf map like struct bpf_map map; So I can't de-reference a pointer(which I have struct bpf_map of) to get fd and max extries which stored in struct within struct.
First So is there any way I can verify what returned from these two function are valids. is including libbpf.h enough for including libbpf_get_error function
struct bpf_object *obj=bpf_object__open(filename);
if (libbpf_get_error(obj)) {
printf("ERROR: opening BPF object file failed\n");
}
struct bpf_map *map=bpf_object__find_map_by_name(obj,"hash_map");
if (libbpf_get_error(map)) {
printf("ERROR: finding map\n");
}
And also moving on from here. How exactly can I get members of struct bpf_map and struct bpf_map_def if I have struct bpf_map object
Can anyone please tell me this?
I am interested in fd and max_entries;
If its not supported in ebpf libbpf then is there any value of syscall number in function
long syscall(long number, ...);
and parameters lists that give me this info.
Thanks
No need to dereference the pointers yourself, the structs are opaque on purpose. Just use the relevant functions from libbpf:
LIBBPF_API int bpf_map__fd(const struct bpf_map *map);
LIBBPF_API __u32 bpf_map__max_entries(const struct bpf_map *map);
If its not supported in ebpf libbpf then is there any value of syscall number in function syscall(...)
There is, but this would only work to retrieve the information from the kernel, after the map has been created (which is admittedly what you want since you're looking for the fd anyway). The BPF_MAP_GET_FD_BY_ID subcommand for the bpf() syscall (syscall(__NR_bpf, cmd, attr, size), see man bpf), would give you the relevant file descriptor, provided you know the map id, and the BPF_OBJ_GET_INFO_BY_FD subcommand, with the relevant arguments would allow you to find the maximum number of entries. But you would pretty much end up reimplementing libbpf.

copy_from_user gives null pointer

I'm attempting to write an Open RG kernel module that, at given intervals, sends a message up to the user space. To this end, I need the kernel to hold a pointer to a static buffer in the user space where this message will be stored. I'm having trouble sending the pointer to the kernel.
The user space function call is something like this (simplified, obviously):
typedef struct {
char msg[MAX_BOOT_MSG];
} msg_t;
static msg_t common_mem;
void user_space_func() {
openrg_module_ctrl(KOS_CDT_TEST, TEST_IOCTL_SET_COMMON_MEM, &common_mem.msg);
}
The kernel space usage is like this:
static void* msg_write;
static int do_ioctl(kos_chardev_t *context, unsigned int cmd,
unsigned long data) {
switch (cmd)
{
case TEST_IOCTL_SET_COMMON_MEM:
received_ioctl = 1;
int ret = copy_from_user(&msg_write, (void *)data, sizeof(char*));
printk("setting common mem to %p, received %d\n", msg_write, ret);
return 0;
}
default:
return -1;
}
The output is setting common mem to 0000000000000000, received 0. I see that common_mem.msg isn't NULL. Any idea what I'm doing wrong?
data is the address of the buffer, so by reading from that address, you are copying the contents of the buffer.
Please note that memory in user space can be moved or swapped out, so this address is valid only for the duration of the system call; you must not store the address for later usage.
Better allocate some memory in your driver, and allow the application to access it with mmap.

How to know free space and total space on nfs share through C program?

I want to know the free space and total space on a nfs share.
I am using ubuntu linux computers for this.
I can do that through commands but I need a C program for this.
I looked into libnfs.h, it contains some functions declarations that I think can be used :
EXTERN int nfs_stat(struct nfs_context *nfs, const char *path, struct stat *st);
EXTERN int nfs_fstat(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *st);
EXTERN int nfs_statvfs(struct nfs_context *nfs, const char *path, struct statvfs *svfs);
But I don't know which one should be used and what to pass for the first parameter(what is context?)
Please help.
Thanks for help in advance.
As #remyabel suggested, I wrote following:
#include<sys/time.h>
#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/statvfs.h>
#include<nfsc/libnfs.h>
#define MAX 63
int main()
{
struct nfs_context *nfs = NULL;
struct statvfs st;
char path[MAX];
strcpy(path,"nfs://192.168.2.73/home/sumit/music2/");
nfs = nfs_init_context();
int ret;
ret = nfs_mount(nfs, "192.168.2.73", path);
perror("Err1");
ret = nfs_statvfs(nfs, "//", &st);
printf("\nret=%d",ret);
printf("\nf_bsize= %lu",st.f_bsize);
printf("\nf_frsize= %lu",st.f_frsize);
printf("\nf_blocks= %lu",st.f_blocks);
printf("\nf_bfree= %lu\n",st.f_bfree);
return 0;
}
Now it works :)
There's a lot more to it, waiting for responses and so on. I had exactly the same problem a few months ago when i wanted to write a nagios plugin to check the space on a filesystem that isn't already mounted. The source code is available at http://www.gbl-software.de/nagiosbinaries/check_nfs/check_nfs-src.tgz, feel free to use and modify as you wish. This uses the NFS libraries from nfsreplay and has the advantage that it's ready to compile for Linux, Solaris and AIX.
Note that, for most NFS servers, your program needs to be suid root to be able to use a reserved port (<1024), because NFS servers won't talk to any port for security reasons.
First you declare the context at the beginning of your program:
struct nfs_context *nfs = NULL;
Here we'll hold the information you want:
struct statvfs st;
Then we initialize the context:
nfs = nfs_init_context();
Mount the share:
struct client client;
client.server = server;
client.export = path;
client.is_finished = 0;
ret = nfs_mount(nfs, client.server, client.export);
And you can use nfs_statvfs like so;
ret = nfs_statvfs(nfs, path, &st);
Where nfs is the context from earlier, path is some filename or directory, and st is the struct that will hold the information. ret contains errno if there was a problem.
Here's statvfs:
struct statvfs {
uint32_t f_bsize;
uint32_t f_frsize;
uint64_t f_blocks;
uint64_t f_bfree;

Where do you get inode functionality from?

I've got some linux drivers I'm trying to port from linux 2.4 to 3.0. During this lengthy span of time, the argument list of ioctl (unlocked_ioctl now) changed a bit:
-static int can_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+static long can_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
The code was using the inode to get the minor version and was passing it to some other commands. Now that inode isn't a "free-be" given in the ioctl parameter list, how can I get it?
Is it possible to derive from the file pointer? or should I "save" a global pointer to it when it shows up in the _open() method? I'd rather avoid that if there's a better way.
You can get the inode from struct file * file in the kernel space. here is the brief struct map
file->f_path.d.dentry->d_inode; you can find the definition followed.
for dentry struct in dcache.h
struct dentry {
atomic_t d_count;
unsigned int d_flags; /* protected by d_lock */
spinlock_t d_lock; /* per dentry lock */
int d_mounted;
struct inode *d_inode; /* Where the name belongs to - NULL is
* negative */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* lookup hash list */
for the file structure in fs.h
file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
...
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op;
spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */
#ifdef CONFIG_SMP
oop, just figured it out by poking around the kernel and looking at other drivers (don't know why it didn't occur to me to do that before). In case anyone else is interested you can get the inode from the file pointer passed into the ioctl as such:
long can_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file->f_path.dentry->d_inode;
If anyone knows why this is a bad idea (I just took it from another driver), or if there's a better/preferred way let me know.

char device catch multiple (int) ioctl-arguments

I have to write a linux char device, which handles ioctl (without BKL) functions per unlock_ioctl. At the moment I can receive one argument from the userspace ioctl command per
__get_user(myint, (int __user *) arg);
How can I receive multiple int-arguments (for example this call)?:
ioctl(fp, SZ_NEW_DEV_FORMAT, 0, 1, 30);
Yes, you have to use structures. For a particular ioctl command there will be some predefined arguments. You need to wrap these all arguments into a structure object and pass in the address of the object. In side the kernel, you need to type cast the given arg to structure pointer and access the arguments. For instance.
struct mesg {
int size;
char buf[100];
};
struct mesg msg1;
/*Fill in the structure object here and call ioctl like this*/
ret = ioctl(fd, SZ_NEW_DEV_FORMAT, &msg1);
Inside the kernel you access it like this:
struct mesg *msg;
copy_from_user((char *)msg, (char *)arg, sizeof(*msg));

Resources