How to IOCTL properly from golang
Asked Answered
C

2

5

I am trying to port bit of raspberrypi's userspace code from C to golang and I've run into a program involving ioctl().

I am having trouble specifically with following C code

#define MAJOR_NUM 100
#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *)
static int mbox_property(int file_desc, void *buf){

   int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf);
   return ret_val;
}

and my go equivalent for this is

func mBoxProperty(f *os.File, buf [256]int64) {
        err := Ioctl(f.Fd(), IOWR(100, 0, 8), uintptr(unsafe.Pointer(&buf[0])))

        if err != nil {
                log.Fatalln("mBoxProperty() : ", err)
        }

}

func Ioctl(fd, op, arg uintptr) error {
        _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, fd, op, arg)
        if ep != 0 {
                return syscall.Errno(ep)
        }
        return nil
}

func IOWR(t, nr, size uintptr) uintptr {
        return IOC(IocRead|IocWrite, t, nr, size)
}
func IOC(dir, t, nr, size uintptr) uintptr {
        return (dir << IocDirshift) | (t << IocTypeshift) | (nr << IocNrshift) | (size << IocSizeshift)
}

but whenever I run this, I get invalid argument error, I think it might be due how I am calling the IOCTL() but I am not sure, how can I fix this?

Corruption answered 27/1, 2019 at 12:24 Comment(1)
Here is an IOCTL go example: github.com/stapelberg/hmgo/blob/master/internal/gpio/reset.goDyedinthewool
T
7

There are ioctl(2) wrappers in "golang.org/x/sys/unix". unix.IoctlSetInt mechanically might meet your needs.

It also looks like you're handing control over a small memory buffer to the kernel. You need to be careful doing this: the Go garbage collector free memory objects that it doesn't think are in use, and even if something is in use, it can move it around. The kernel won't know about this and will keep using the old pointer. The unsafe.Pointer documentation has a fair amount to say on this topic, even with regards to less exotic syscalls. I'm not aware of anything that can "lock" a Go object in memory that would keep it from being moved or deallocated (nothing jumps out in the runtime package, for instance).

You might consider writing a very small extension using cgo that malloc()ed an appropriate buffer and handed it off to the ioctl. malloc'd memory isn't garbage-collected so it won't move or be freed from under you; some low-level tools might think this looks like a memory leak (it wouldn't be a bad idea to keep the old value of the pointer around to be able to free it later and dodge this).

Twig answered 27/1, 2019 at 13:0 Comment(1)
The unsafe.Pointer is exactly the solution for the problem you are describing?!Dyedinthewool
F
4

You might also be falling afoul of the detail that the uintpr(unsafe.Pointer(...)) needs to happen in the call to syscall.Syscall.

Here are the details, from https://golang.org/pkg/unsafe/#Pointer

(4) Conversion of a Pointer to a uintptr when calling syscall.Syscall.

The Syscall functions in package syscall pass their uintptr arguments directly to the operating system, which then may, depending on the details of the call, reinterpret some of them as pointers. That is, the system call implementation is implicitly converting certain arguments back from uintptr to pointer.

If a pointer argument must be converted to uintptr for use as an argument, that conversion must appear in the call expression itself:

syscall.Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(n))

The compiler handles a Pointer converted to a uintptr in the argument list of a call to a function implemented in assembly by arranging that the referenced allocated object, if any, is retained and not moved until the call completes, even though from the types alone it would appear that the object is no longer needed during the call.

For the compiler to recognize this pattern, the conversion must appear in the argument list:

// INVALID: uintptr cannot be stored in variable
// before implicit conversion back to Pointer during system call.
u := uintptr(unsafe.Pointer(p))
syscall.Syscall(SYS_READ, uintptr(fd), u, uintptr(n))

This use of unsafe is the trick that will let you "lock" the Go object that Dave Maze was looking for above.

Fiske answered 30/8, 2019 at 18:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.