I am working on Pintos Project # 2. I have implemented most of the system calls. In exec system call, there is a test exec-missing which according to comment in file checks this:
/* Tries to execute a nonexistent process.
The exec system call must return -1. */
#include <syscall.h>
#include "tests/lib.h"
#include "tests/main.h"
void
test_main (void)
{
msg ("exec(\"no-such-file\"): %d", exec ("no-such-file"));
}
I cant figure our how to check this in my exec code. I have put a check on the frame pointer correctly, what could be missing ?
The executable file specified as the first argument for "exec" is loaded from the load() function present in the start_process() function.
To remind you, start_process() is where function which is run as part of the newly created process. This function is responsible for loading the executable onto the memory and start executing it.
If the executable file is not found, then the load() function will report an error by returning 0. After this, you need to deallocate all the memory which was allocated for the process and end it by calling the exit() system call or by calling the process_exit() function.
You can start by debugging the return status of the load() function.
Related
Where does the verifier print its messages? I have a simple code embedded in struct bpf_insn which I load and attach as BPF_PROG_TYPE_SOCKET_FILTER type:
struct bpf_insn prog[] = {
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
BPF_EXIT_INSN(),
};
This code is intentionally made wrong (R0 is not initialized before the exit). bpf_prog_load() returns EACCESS error and fails to load, which is expected, but I wanted to the verifier messages (nothing in dmesg or console).
When attempting to load an eBPF program, it is up to the loader to pass a buffer to the kernel verifier and to later print it to get the verifier's output.
The verifier will use this buffer provided by the user space program and print all its logs in it. Excepted for a very few specific messages, it will not print anything to the kernel logs or to the console (which is handled by your shell, not the kernel directly).
Let's have a look at a snippet from samples/bpf/sock_example.c, that you mentioned in the comments.
prog_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog, insns_cnt,
"GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE);
if (prog_fd < 0) {
printf("failed to load prog '%s'\n", strerror(errno));
goto cleanup;
}
This is the part where we attempt to load the program. We call bpf_load_program() from libbpf, and we pass it, in this order, the program type, the instructions, the number of instructions, the license string, some flag related to kernel versions, and at last: an empty buffer and its size. The size BPF_LOG_BUF_SIZE is non-null (defined in tools/lib/bpf/bpf as (UINT32_MAX >> 8)).
The function bpf_load_program() will pass all this information, including the pointer to the buffer, to the bpf() system call, which will attempt to load the program. The verifier will populate the buffer with logs (whether the load succeeds or not, but see note at the bottom). Then it is up to the loader program, again, to use these logs. The function bpf_load_program() is low-level, it does nothing with the verifier's logs in the buffer, even on failure to load. It leaves it to the caller to process or dump the logs. The sample application that you attempt to run does nothing either; therefore, the buffer is unused, and you don't get to see the logs in the console.
To see the logs, in your case, you probably just need to dump this buffer. Something as simple as the following should work:
...
if (prog_fd < 0) {
printf("failed to load prog '%s'\n", strerror(errno));
printf("%s", bpf_log_buf);
goto cleanup;
}
Note: In addition to the buffer and the size of the buffer, the loader must pass a log_level integer to the verifier, to tell it what level of verbosity it should use. If the value is at 0, the verifier prints nothing to the buffer. In the current case, we do not handle the log_level directly. bpf_load_program() does not either and sets the value to 0, but it ends up calling libbpf__bpf_prog_load() in libbpf. That function tries to load the program a first time without changing the log_level, but in case of failure, it does a new attempt with the log_level set at 1 - See Mark's pointers in the comments for details. The different values for log_level are defined in internal kernel headers and are not part of the user API, meaning the behaviour of the verifier regarding log verbosity may vary between kernel versions.
I'm trying to retrieve the serial number from a drive using the MC_BR_GetHardwareInfo function block. Since the documentation lacks any kind of example code on this topic I'm getting nowhere.
Which information should I provide to the function block in order to get the desired serial number?
Below sample will crash in the PLC, probably because the function block requires certain pointers to be addressed:
MC_HARDWARE_INFO_REF hwinfo;
MC_BR_GetHardwareInfo(&hwinfo);
You are probably getting a page fault, because you provide the MC_BR_GetHardwareInfo function block (FUB) a wrong type, which leads to random behavior.
A function block is basically a function which requires a reference to a specific type as parameter. This type contains the actual in- and outputs which are used, internal state variables, etc. We need this, because of the synchronous execution of the code. This means unlike a function, you need to call a FUB until it is done.
Let's take a look to the help of the FUB:
Guid: 056444ea-2a15-4af6-a5ae-0675894b17d3
So the FUB needs a reference to the Axis object of which you want to know the HW info and an Execute command. It will give you some status bits, an error code and the actual data you want to have within the structure HardwareInfo of the type MC_HARDWARE_INFO_REF.
First we need to instantiate the FUB by create a variable of its type. We do this in the local *.var file of the task:
VAR
fbGetHwInfo : MC_BR_GetHardwareInfo := (0);
END_VAR
Then we call set the parameters of the FUB and call it, which might look like this:
void _CYCLIC ProgramCyclic(void)
{
//should be set by the application or in watch/monitor; now it only
//executes once
fbGetHwInfo.Execute = 1;
//reference to your axis object; when using a wizard the first axis
//will be gAxis01 on default
fbGetHwInfo.Axis = (UDINT)&gAxis01;
//call the FUB
MC_BR_GetHardwareInfo(&fbGetHwInfo);
if(fbGetHwInfo.Error == 1)
{
//TODO: errorhandling
}
else if(fbGetHwInfo.Done == 1)
{
//TODO use output
//fbGetHwInfo.HardwareInfo
}
}
typically you would do this in some statemachine. Also you probably have to wait until the network to the drive is initialized. You could check this with the MC_BR_ReadDriveStatus FUB. Just for testing it should be enough to wait for some seconds after reboot and set the Execute flag in monitor mode.
I am learning how to write kernel drivers, and I have a doubt about the proper
usage of the function unregister_chrdev_region.
Right now I have a simple test module with just the init and
exit functions:
static dev_t devn;
int __init my_dev_init(void)
{
devn = MKDEV(0,0);
if(alloc_chrdev_region(&devn,0,1,"my_dev") != 0)
{
return -EBUSY;
}
else
{
return 0;
}
}
void __exit my_dev_exit(void)
{
unregister_chrdev_region(devn,1);
}
My question: is it safe to call unregister_chrdev_region if the registration failed ? I would assume no, but pretty much all example code that I have seen calls unregister_chrdev_region no matter what happened during the initialization.
Registration rarely fails. If registration fails, init function fails, thus insmod command will not succeed in loading the kernel module i.e. test.ko and device node will not be created. So there is no question of unregistration (rmmod). If registration is successful then only we can unregister the driver i.e. unloading the kernel, removal of device node etc... using rmmod. If in init function registration is successful and some other API fails then need to add goto statements, jump to appropriate goto label to unregister and return the appropriate error value. Hope I have cleared your doubt :-)
I am calling a driver function from application by using an ioctl(). By using logs i found that my kernel code is executing first. for example,
In my application
printf("Calling Driver");
ioctl();
In my driver
printk("Driver called");
When i execute my code, first it is printing "Driver called" and then "Calling Driver"
I there any reason for this?
By default, the stdout stream is buffered, and the datas are only displayed after a newline (or the end of execution). In this case, the display is done after the ioctl call.
Your can force the display using fflush after your printf call.
printf("Calling Driver");
fflush(stdout);
ioctl();
For more details, you can read these answers.
I want to implement the already defined system calls in PintOS ( halt(), create()...etc defined in pintos/src/lib/user/syscall.c ). The current system call handler in pintos/src/userprog/syscall.c does not do anything. How do I make a process that makes system calls. Further I need to myself add a few system calls. How do I proceed in that too. But first I need to implement the existing system calls.
The default implementation in pintos terminates the calling process.
goto this link.There is explanation on where to modify the code to implement the system calls.
The "src/examples" directory contains a few sample user programs.
The "Makefile" in this directory compiles the provided examples, and you can edit it compile your own programs as well.
This program/process when run will inturn make a system call.
Use gdb to follow the execution of one such program a simple printf statement will eventually call write system call to STDOUT file.
The link given also has pointers on how to run pintos on gdb, my guess is you are using either bochs or qemu.In any case just run the gdb once with a simple hello world program running on pintos.
This will give u an idea of how the system call is made.
static void
syscall_handler (struct intr_frame *f)// UNUSED)
{
int *p=f->esp;
switch(*p)
case *p=SYS_CREATE // NUMBER # DEFINED
const char *name=*(p+1); //extract the filename
if(name==NULL||*name==NULL)
exit(-1);
off_t size=(int32_t)*(p+2);//extract file size
f->eax=filesys_create(name,size,_FILE); //call filesys_create
//eax will have the return value
}
This is pseudo code for sys_create .. all file system related system call are very trivial,
Filesys realted system calls like open read write close needs you to translate file to their corresponding fd (file descriptor). You need to add a file table for each process to keep track this, this can either be preprocess data or a global data.(UR choice),
case (*p==SYS_WRITE)
{
// printf("wite syscall\n");
char *buffer=*(p+2);
unsigned size=*(p+3);
int fd=*(p+1);
// getiing the fd of specified file
struct file *fil= thread_current()->fdtable[fd];/ my per thread fdtable
if(fd==1) goto here;
if(is_directory(fil->inode)){
exit(-1);
goto done;
}
here:
if(buffer>=PHYS_BASE)exit(-1);
if(fd<0||fd>=128){exit(-1);}
if(fd==0){exit(-1);} // writing to STDIN
if(fd==1) //writing to STDOUT
{
int a=(int)size;
while(a>=100)
{
putbuf(buffer,100);
buffer=buffer+100;
a-=100;
}
putbuf(buffer,a);
f->eax=(int)size;
}
else
if(thread_current()->fdtable[fd]==NULL)
{f->eax=-1;}
else
{
f->eax=file_write(thread_current()->fdtable[fd],buffer,(off_t)size);
}
done: ;
}//printf("write");} /* Write to a file. */
Open - adds anew entry to fdtable and return the fd number u give to the file,
close - remove that entry from fd table
read - similar to write.
The process_create ,wait are not simple to implement...
Cheers :)