root/src/add-ons/kernel/drivers/misc/poke.cpp
/*
 * Copyright 2005, Oscar Lesta. All rights reserved.
 * Copyright 2018-2023, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */
#include "poke.h"

#include <Drivers.h>
#include <KernelExport.h>
#include <ISA.h>
#include <PCI.h>

#include <team.h>
#include <vm/vm.h>

#if defined(__i386__) || defined(__x86_64__)
#include <thread.h>
#endif


static status_t poke_open(const char*, uint32, void**);
static status_t poke_close(void*);
static status_t poke_free(void*);
static status_t poke_control(void*, uint32, void*, size_t);
static status_t poke_read(void*, off_t, void*, size_t*);
static status_t poke_write(void*, off_t, const void*, size_t*);


static const char* poke_name[] = {
    "misc/" POKE_DEVICE_NAME,
    NULL
};


device_hooks poke_hooks = {
        poke_open,
        poke_close,
        poke_free,
        poke_control,
        poke_read,
        poke_write,
};

int32 api_version = B_CUR_DRIVER_API_VERSION;

isa_module_info* isa;
pci_module_info* pci;


status_t
init_hardware(void)
{
        return B_OK;
}


status_t
init_driver(void)
{
        if (get_module(B_ISA_MODULE_NAME, (module_info**)&isa) < B_OK)
                return ENOSYS;

        if (get_module(B_PCI_MODULE_NAME, (module_info**)&pci) < B_OK) {
                put_module(B_ISA_MODULE_NAME);
                return ENOSYS;
        }

        return B_OK;
}


void
uninit_driver(void)
{
        put_module(B_ISA_MODULE_NAME);
        put_module(B_PCI_MODULE_NAME);
}


const char**
publish_devices(void)
{
        return poke_name;
}


device_hooks*
find_device(const char* name)
{
        return &poke_hooks;
}


//      #pragma mark -


status_t
poke_open(const char* name, uint32 flags, void** cookie)
{
        *cookie = NULL;

        if (getuid() != 0 && geteuid() != 0)
                return EPERM;

#if defined(__i386__) || defined(__x86_64__)
        /* on x86, raise the IOPL so that outb/inb will work */
        iframe* frame = x86_get_user_iframe();
        int iopl = 3;
        frame->flags &= ~X86_EFLAGS_IO_PRIVILEG_LEVEL;
        frame->flags |= (iopl << X86_EFLAGS_IO_PRIVILEG_LEVEL_SHIFT)
                & X86_EFLAGS_IO_PRIVILEG_LEVEL;
#endif

        return B_OK;
}


status_t
poke_close(void* cookie)
{
        return B_OK;
}


status_t
poke_free(void* cookie)
{
#if defined(__i386__) || defined(__x86_64__)
        iframe* frame = x86_get_user_iframe();
        int iopl = 0;
        frame->flags &= ~X86_EFLAGS_IO_PRIVILEG_LEVEL;
        frame->flags |= (iopl << X86_EFLAGS_IO_PRIVILEG_LEVEL_SHIFT)
                & X86_EFLAGS_IO_PRIVILEG_LEVEL;
#endif

        return B_OK;
}


status_t
poke_control(void* cookie, uint32 op, void* arg, size_t length)
{
        if (!IS_USER_ADDRESS(arg))
                return B_BAD_ADDRESS;

        switch (op) {
                case POKE_PORT_READ:
                {
                        status_t result = B_OK;
                        port_io_args ioctl;
                        if (user_memcpy(&ioctl, arg, sizeof(port_io_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        if (ioctl.signature != POKE_SIGNATURE)
                                return B_BAD_VALUE;

                        switch (ioctl.size) {
                                case 1:
                                        ioctl.value = isa->read_io_8(ioctl.port);
                                break;
                                case 2:
                                        ioctl.value = isa->read_io_16(ioctl.port);
                                break;
                                case 4:
                                        ioctl.value = isa->read_io_32(ioctl.port);
                                break;
                                default:
                                        result = B_BAD_VALUE;
                        }

                        if (user_memcpy(arg, &ioctl, sizeof(port_io_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        return result;
                }

                case POKE_PORT_WRITE:
                {
                        status_t result = B_OK;
                        port_io_args ioctl;
                        if (user_memcpy(&ioctl, arg, sizeof(port_io_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        if (ioctl.signature != POKE_SIGNATURE)
                                return B_BAD_VALUE;

                        switch (ioctl.size) {
                                case 1:
                                        isa->write_io_8(ioctl.port, ioctl.value);
                                        break;
                                case 2:
                                        isa->write_io_16(ioctl.port, ioctl.value);
                                        break;
                                case 4:
                                        isa->write_io_32(ioctl.port, ioctl.value);
                                        break;
                                default:
                                        result = B_BAD_VALUE;
                        }

                        return result;
                }

                case POKE_PORT_INDEXED_READ:
                {
                        port_io_args ioctl;
                        if (user_memcpy(&ioctl, arg, sizeof(port_io_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        if (ioctl.signature != POKE_SIGNATURE)
                                return B_BAD_VALUE;

                        isa->write_io_8(ioctl.port, ioctl.size);
                        ioctl.value = isa->read_io_8(ioctl.port + 1);

                        if (user_memcpy(arg, &ioctl, sizeof(port_io_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        return B_OK;
                }

                case POKE_PORT_INDEXED_WRITE:
                {
                        port_io_args ioctl;
                        if (user_memcpy(&ioctl, arg, sizeof(port_io_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        if (ioctl.signature != POKE_SIGNATURE)
                                return B_BAD_VALUE;

                        isa->write_io_8(ioctl.port, ioctl.size);
                        isa->write_io_8(ioctl.port + 1, ioctl.value);
                        return B_OK;
                }

                case POKE_PCI_READ_CONFIG:
                {
                        pci_io_args ioctl;
                        if (user_memcpy(&ioctl, arg, sizeof(pci_io_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        if (ioctl.signature != POKE_SIGNATURE)
                                return B_BAD_VALUE;

                        ioctl.value = pci->read_pci_config(ioctl.bus, ioctl.device,
                                ioctl.function, ioctl.offset, ioctl.size);
                        if (user_memcpy(arg, &ioctl, sizeof(pci_io_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        return B_OK;
                }

                case POKE_PCI_WRITE_CONFIG:
                {
                        pci_io_args ioctl;
                        if (user_memcpy(&ioctl, arg, sizeof(pci_io_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        if (ioctl.signature != POKE_SIGNATURE)
                                return B_BAD_VALUE;

                        pci->write_pci_config(ioctl.bus, ioctl.device, ioctl.function,
                                ioctl.offset, ioctl.size, ioctl.value);
                        return B_OK;
                }

                case POKE_GET_NTH_PCI_INFO:
                {
                        pci_info_args ioctl;
                        if (user_memcpy(&ioctl, arg, sizeof(pci_info_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        if (ioctl.signature != POKE_SIGNATURE)
                                return B_BAD_VALUE;
                        if (!IS_USER_ADDRESS(ioctl.info))
                                return B_BAD_ADDRESS;

                        pci_info info;
                        ioctl.status = pci->get_nth_pci_info(ioctl.index, &info);

                        if (user_memcpy(ioctl.info, &info, sizeof(pci_info)) != B_OK)
                                return B_BAD_ADDRESS;
                        if (user_memcpy(arg, &ioctl, sizeof(pci_info_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        return B_OK;
                }

                case POKE_GET_PHYSICAL_ADDRESS:
                {
                        mem_map_args ioctl;
                        if (user_memcpy(&ioctl, arg, sizeof(mem_map_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        physical_entry table;
                        status_t result;

                        if (ioctl.signature != POKE_SIGNATURE)
                                return B_BAD_VALUE;

                        result = get_memory_map(ioctl.address, ioctl.size, &table, 1);
                        ioctl.physical_address = table.address;
                        ioctl.size = table.size;
                        if (user_memcpy(arg, &ioctl, sizeof(mem_map_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        return result;
                }

                case POKE_MAP_MEMORY:
                {
                        mem_map_args ioctl;
                        if (user_memcpy(&ioctl, arg, sizeof(mem_map_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        if (ioctl.signature != POKE_SIGNATURE)
                                return B_BAD_VALUE;

                        char name[B_OS_NAME_LENGTH];
                        if (user_strlcpy(name, ioctl.name, B_OS_NAME_LENGTH) < B_OK)
                                return B_BAD_ADDRESS;

                        ioctl.area = vm_map_physical_memory(team_get_current_team_id(), name,
                                (void**)&ioctl.address, ioctl.flags, ioctl.size, ioctl.protection,
                                ioctl.physical_address, false);

                        if (user_memcpy(arg, &ioctl, sizeof(mem_map_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        return ioctl.area;
                }

                case POKE_UNMAP_MEMORY:
                {
                        mem_map_args ioctl;
                        if (user_memcpy(&ioctl, arg, sizeof(mem_map_args)) != B_OK)
                                return B_BAD_ADDRESS;
                        if (ioctl.signature != POKE_SIGNATURE)
                                return B_BAD_VALUE;

                        return _user_delete_area(ioctl.area);
                }
        }

        return B_BAD_VALUE;
}


status_t
poke_read(void* cookie, off_t position, void* buffer, size_t* numBytes)
{
        *numBytes = 0;
        return B_NOT_ALLOWED;
}


status_t
poke_write(void* cookie, off_t position, const void* buffer, size_t* numBytes)
{
        *numBytes = 0;
        return B_NOT_ALLOWED;
}