root/headers/private/kernel/util/iovec_support.h
/*
 * Copyright 2022, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT license.
 */
#ifndef _UTIL_IOVEC_SUPPORT_H
#define _UTIL_IOVEC_SUPPORT_H


#include <KernelExport.h>


typedef struct generic_io_vec {
        generic_addr_t  base;
        generic_size_t  length;
} generic_io_vec;


#ifdef _KERNEL_VM_VM_H

static inline status_t
generic_memcpy(generic_addr_t dest, bool destPhysical, generic_addr_t src, bool srcPhysical,
        generic_size_t size, bool user = false)
{
        if (!srcPhysical && !destPhysical) {
                if (user)
                        return user_memcpy((void*)dest, (void*)src, size);
                memcpy((void*)dest, (void*)src, size);
                return B_OK;
        } else if (destPhysical && !srcPhysical) {
                return vm_memcpy_to_physical(dest, (const void*)src, size, user);
        } else if (!destPhysical && srcPhysical) {
                return vm_memcpy_from_physical((void*)dest, src, size, user);
        }

        panic("generic_memcpy: physical -> physical not supported!");
        return B_NOT_SUPPORTED;
}

#endif


#ifdef IS_USER_ADDRESS

/*!
 * Copies an array of `iovec`s from userland.
 * Callers must verify vecCount <= IOV_MAX and supply their own vecs buffer.
 */
static inline status_t
get_iovecs_from_user(const iovec* userVecs, size_t vecCount, iovec* vecs,
        bool permitNull = false)
{
        if (vecCount == 0)
                return B_BAD_VALUE;

        if (!IS_USER_ADDRESS(userVecs))
                return B_BAD_ADDRESS;

        if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) != B_OK)
                return B_BAD_ADDRESS;

        size_t total = 0;
        for (size_t i = 0; i < vecCount; i++) {
                if (permitNull && vecs[i].iov_base == NULL)
                        continue;
                if (!is_user_address_range(vecs[i].iov_base, vecs[i].iov_len)) {
                        return B_BAD_ADDRESS;
                }
                if (vecs[i].iov_len > SSIZE_MAX || total > (SSIZE_MAX - vecs[i].iov_len)) {
                        return B_BAD_VALUE;
                }
                total += vecs[i].iov_len;
        }

        return B_OK;
}

#endif


#endif  // _UTIL_IOVEC_SUPPORT_H