blob: 57b50e30120ec0df0ecf5511cc19a24bfe199ca8 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <err.h>
#include <platform.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <trace.h>
#include <dev/interrupt.h>
#include <dev/udisplay.h>
#include <kernel/vm.h>
#include <kernel/vm/vm_object_paged.h>
#include <kernel/vm/vm_object_physical.h>
#include <lib/user_copy.h>
#include <lib/user_copy/user_ptr.h>
#if ARCH_X86
#include <platform/pc/bootloader.h>
#endif
#include <magenta/handle_owner.h>
#include <magenta/interrupt_dispatcher.h>
#include <magenta/interrupt_event_dispatcher.h>
#include <magenta/magenta.h>
#include <magenta/process_dispatcher.h>
#include <magenta/syscalls/pci.h>
#include <magenta/user_copy.h>
#include <magenta/vm_object_dispatcher.h>
#include "syscalls_priv.h"
#define LOCAL_TRACE 0
static_assert(MX_CACHE_POLICY_CACHED == ARCH_MMU_FLAG_CACHED,
"Cache policy constant mismatch - CACHED");
static_assert(MX_CACHE_POLICY_UNCACHED == ARCH_MMU_FLAG_UNCACHED,
"Cache policy constant mismatch - UNCACHED");
static_assert(MX_CACHE_POLICY_UNCACHED_DEVICE == ARCH_MMU_FLAG_UNCACHED_DEVICE,
"Cache policy constant mismatch - UNCACHED_DEVICE");
static_assert(MX_CACHE_POLICY_WRITE_COMBINING == ARCH_MMU_FLAG_WRITE_COMBINING,
"Cache policy constant mismatch - WRITE_COMBINING");
mx_status_t sys_interrupt_create(mx_handle_t hrsrc, uint32_t vector, uint32_t options,
user_ptr<mx_handle_t> out_handle) {
LTRACEF("vector %u options 0x%x\n", vector, options);
mx_status_t status;
if ((status = validate_resource_irq(hrsrc, vector)) < 0) {
return status;
}
mxtl::RefPtr<Dispatcher> dispatcher;
mx_rights_t rights;
mx_status_t result = InterruptEventDispatcher::Create(vector, options, &dispatcher, &rights);
if (result != MX_OK)
return result;
HandleOwner handle(MakeHandle(mxtl::move(dispatcher), rights));
auto up = ProcessDispatcher::GetCurrent();
mx_handle_t hv = up->MapHandleToValue(handle);
if (out_handle.copy_to_user(hv) != MX_OK) {
return MX_ERR_INVALID_ARGS;
}
up->AddHandle(mxtl::move(handle));
return MX_OK;
}
mx_status_t sys_interrupt_complete(mx_handle_t handle_value) {
LTRACEF("handle %x\n", handle_value);
auto up = ProcessDispatcher::GetCurrent();
mxtl::RefPtr<InterruptDispatcher> interrupt;
mx_status_t status = up->GetDispatcher(handle_value, &interrupt);
if (status != MX_OK)
return status;
return interrupt->InterruptComplete();
}
mx_status_t sys_interrupt_wait(mx_handle_t handle_value) {
LTRACEF("handle %x\n", handle_value);
auto up = ProcessDispatcher::GetCurrent();
mxtl::RefPtr<InterruptDispatcher> interrupt;
mx_status_t status = up->GetDispatcher(handle_value, &interrupt);
if (status != MX_OK)
return status;
return interrupt->WaitForInterrupt();
}
mx_status_t sys_interrupt_signal(mx_handle_t handle_value) {
LTRACEF("handle %x\n", handle_value);
auto up = ProcessDispatcher::GetCurrent();
mxtl::RefPtr<InterruptDispatcher> interrupt;
mx_status_t status = up->GetDispatcher(handle_value, &interrupt);
if (status != MX_OK)
return status;
return interrupt->UserSignal();
}
mx_status_t sys_vmo_create_contiguous(mx_handle_t hrsrc, size_t size,
uint32_t alignment_log2,
user_ptr<mx_handle_t> _out) {
LTRACEF("size 0x%zu\n", size);
if (size == 0) return MX_ERR_INVALID_ARGS;
if (alignment_log2 == 0)
alignment_log2 = PAGE_SIZE_SHIFT;
// catch obviously wrong values
if (alignment_log2 < PAGE_SIZE_SHIFT ||
alignment_log2 >= (8 * sizeof(uint64_t)))
return MX_ERR_INVALID_ARGS;
// TODO(MG-971): finer grained validation
mx_status_t status;
if ((status = validate_resource(hrsrc, MX_RSRC_KIND_ROOT)) < 0) {
return status;
}
size = ROUNDUP_PAGE_SIZE(size);
// create a vm object
mxtl::RefPtr<VmObject> vmo;
status = VmObjectPaged::Create(PMM_ALLOC_FLAG_ANY, size, &vmo);
if (status != MX_OK)
return status;
// always immediately commit memory to the object
uint64_t committed;
// CommitRangeContiguous takes a uint8_t for the alignment
auto align_log2_arg = static_cast<uint8_t>(alignment_log2);
status = vmo->CommitRangeContiguous(0, size, &committed, align_log2_arg);
if (status < 0 || (size_t)committed < size) {
LTRACEF("failed to allocate enough pages (asked for %zu, got %zu)\n", size / PAGE_SIZE,
(size_t)committed / PAGE_SIZE);
return MX_ERR_NO_MEMORY;
}
// create a Vm Object dispatcher
mxtl::RefPtr<Dispatcher> dispatcher;
mx_rights_t rights;
mx_status_t result = VmObjectDispatcher::Create(mxtl::move(vmo), &dispatcher, &rights);
if (result != MX_OK)
return result;
// create a handle and attach the dispatcher to it
HandleOwner handle(MakeHandle(mxtl::move(dispatcher), rights));
if (!handle)
return MX_ERR_NO_MEMORY;
auto up = ProcessDispatcher::GetCurrent();
if (_out.copy_to_user(up->MapHandleToValue(handle)) != MX_OK)
return MX_ERR_INVALID_ARGS;
up->AddHandle(mxtl::move(handle));
return MX_OK;
}
mx_status_t sys_vmo_create_physical(mx_handle_t hrsrc, uintptr_t paddr, size_t size,
user_ptr<mx_handle_t> _out) {
LTRACEF("size 0x%zu\n", size);
// TODO: attempting to create a physical VMO that points to memory should be an error
mx_status_t status;
if ((status = validate_resource_mmio(hrsrc, paddr, size)) < 0) {
return status;
}
size = ROUNDUP_PAGE_SIZE(size);
// create a vm object
mxtl::RefPtr<VmObject> vmo;
mx_status_t result = VmObjectPhysical::Create(paddr, size, &vmo);
if (result != MX_OK) {
return result;
}
// create a Vm Object dispatcher
mxtl::RefPtr<Dispatcher> dispatcher;
mx_rights_t rights;
result = VmObjectDispatcher::Create(mxtl::move(vmo), &dispatcher, &rights);
if (result != MX_OK)
return result;
// create a handle and attach the dispatcher to it
HandleOwner handle(MakeHandle(mxtl::move(dispatcher), rights));
if (!handle)
return MX_ERR_NO_MEMORY;
auto up = ProcessDispatcher::GetCurrent();
if (_out.copy_to_user(up->MapHandleToValue(handle)) != MX_OK)
return MX_ERR_INVALID_ARGS;
up->AddHandle(mxtl::move(handle));
return MX_OK;
}
mx_status_t sys_bootloader_fb_get_info(user_ptr<uint32_t> format, user_ptr<uint32_t> width, user_ptr<uint32_t> height, user_ptr<uint32_t> stride) {
#if ARCH_X86
if (!bootloader.fb.base ||
format.copy_to_user(bootloader.fb.format) ||
width.copy_to_user(bootloader.fb.width) ||
height.copy_to_user(bootloader.fb.height) ||
stride.copy_to_user(bootloader.fb.stride)) {
return MX_ERR_INVALID_ARGS;
} else {
return MX_OK;
}
#else
return MX_ERR_NOT_SUPPORTED;
#endif
}
mx_status_t sys_set_framebuffer(mx_handle_t hrsrc, user_ptr<void> vaddr, uint32_t len, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
// TODO(MG-971): finer grained validation
mx_status_t status;
if ((status = validate_resource(hrsrc, MX_RSRC_KIND_ROOT)) < 0) {
return status;
}
intptr_t paddr = vaddr_to_paddr(vaddr.get());
udisplay_set_framebuffer(paddr, len);
struct display_info di;
memset(&di, 0, sizeof(struct display_info));
di.format = format;
di.width = width;
di.height = height;
di.stride = stride;
di.flags = DISPLAY_FLAG_HW_FRAMEBUFFER;
udisplay_set_display_info(&di);
return MX_OK;
}
mx_status_t sys_set_framebuffer_vmo(mx_handle_t hrsrc, mx_handle_t vmo_handle, uint32_t len, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
mx_status_t status;
if ((status = validate_resource(hrsrc, MX_RSRC_KIND_ROOT)) < 0)
return status;
auto up = ProcessDispatcher::GetCurrent();
// lookup the dispatcher from handle
mxtl::RefPtr<VmObjectDispatcher> vmo;
status = up->GetDispatcher(vmo_handle, &vmo);
if (status != MX_OK)
return status;
status = udisplay_set_framebuffer_vmo(vmo->vmo());
if (status != MX_OK)
return status;
struct display_info di;
memset(&di, 0, sizeof(struct display_info));
di.format = format;
di.width = width;
di.height = height;
di.stride = stride;
di.flags = DISPLAY_FLAG_HW_FRAMEBUFFER;
udisplay_set_display_info(&di);
return MX_OK;
}
#if ARCH_X86
#include <arch/x86/descriptor.h>
#include <arch/x86/ioport.h>
mx_status_t sys_mmap_device_io(mx_handle_t hrsrc, uint32_t io_addr, uint32_t len) {
// TODO(MG-971): finer grained validation
mx_status_t status;
if ((status = validate_resource(hrsrc, MX_RSRC_KIND_ROOT)) < 0) {
return status;
}
LTRACEF("addr 0x%x len 0x%x\n", io_addr, len);
return IoBitmap::GetCurrent().SetIoBitmap(io_addr, len, 1);
}
#else
mx_status_t sys_mmap_device_io(mx_handle_t hrsrc, uint32_t io_addr, uint32_t len) {
// doesn't make sense on non-x86
return MX_ERR_NOT_SUPPORTED;
}
#endif
uint64_t sys_acpi_uefi_rsdp(mx_handle_t hrsrc) {
// TODO(MG-971): finer grained validation
mx_status_t status;
if ((status = validate_resource(hrsrc, MX_RSRC_KIND_ROOT)) < 0) {
return status;
}
#if ARCH_X86
return bootloader.acpi_rsdp;
#endif
return 0;
}
mx_status_t sys_acpi_cache_flush(mx_handle_t hrsrc) {
// TODO(MG-971): finer grained validation
mx_status_t status;
if ((status = validate_resource(hrsrc, MX_RSRC_KIND_ROOT)) < 0) {
return status;
}
// TODO(teisenbe): This should be restricted to when interrupts are
// disabled, but we haven't added support for letting the ACPI process
// disable interrupts yet. It only uses this for S-state transitions
// like poweroff and (more importantly) sleep.
#if ARCH_X86
__asm__ volatile ("wbinvd");
return MX_OK;
#else
return MX_ERR_NOT_SUPPORTED;
#endif
}