* arch/arm64/src/common/arm64_fpu.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
***************************************************************************/
* Included Files
***************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <fcntl.h>
#include <stdio.h>
#include <nuttx/sched.h>
#include <nuttx/arch.h>
#include <nuttx/fs/procfs.h>
#include <arch/barriers.h>
#include <arch/irq.h>
#include "sched/sched.h"
#include "arm64_arch.h"
#include "arm64_fork.h"
#include "arm64_internal.h"
#include "arm64_fatal.h"
#include "arm64_fpu.h"
* Pre-processor Definitions
***************************************************************************/
#define FPU_CALLEE_REGS (8)
#define FPU_PROC_LINELEN (64 * CONFIG_SMP_NCPUS)
* Private Types
***************************************************************************/
#ifdef CONFIG_FS_PROCFS_REGISTER
struct arm64_fpu_procfs_file_s
{
struct procfs_file_s base;
unsigned int linesize;
char line[FPU_PROC_LINELEN];
};
#endif
* Private Data
***************************************************************************/
#ifdef CONFIG_FS_PROCFS_REGISTER
static DEFINE_PER_CPU_BSS_SMP(struct arm64_cpu_fpu_context, g_cpu_fpu_ctx);
static int arm64_fpu_procfs_open(struct file *filep, const char *relpath,
int oflags, mode_t mode);
static int arm64_fpu_procfs_close(struct file *filep);
static ssize_t arm64_fpu_procfs_read(struct file *filep, char *buffer,
size_t buflen);
static int arm64_fpu_procfs_stat(const char *relpath, struct stat *buf);
* We use the old-fashioned kind of initializers so that this will compile
* with any compiler.
*/
const struct procfs_operations arm64_fpu_procfs_operations =
{
arm64_fpu_procfs_open,
arm64_fpu_procfs_close,
arm64_fpu_procfs_read,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
arm64_fpu_procfs_stat
};
static const struct procfs_entry_s g_procfs_arm64_fpu =
{
"fpu",
&arm64_fpu_procfs_operations
};
#endif
* Private Functions
***************************************************************************/
static void arm64_fpu_access_trap_enable(void)
{
uint64_t cpacr;
cpacr = read_sysreg(cpacr_el1);
cpacr &= ~CPACR_EL1_FPEN_NOTRAP;
write_sysreg(cpacr, cpacr_el1);
UP_ISB();
}
static void arm64_fpu_access_trap_disable(void)
{
uint64_t cpacr;
cpacr = read_sysreg(cpacr_el1);
cpacr |= CPACR_EL1_FPEN_NOTRAP;
write_sysreg(cpacr, cpacr_el1);
UP_ISB();
}
#ifdef CONFIG_FS_PROCFS_REGISTER
static int arm64_fpu_procfs_open(struct file *filep, const char *relpath,
int oflags, mode_t mode)
{
struct arm64_fpu_procfs_file_s *priv;
uinfo("Open '%s'\n", relpath);
* access is not permitted.
*
* REVISIT: Write-able proc files could be quite useful.
*/
if (((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0))
{
uerr("ERROR: Only O_RDONLY supported\n");
return -EACCES;
}
priv = kmm_zalloc(sizeof(struct arm64_fpu_procfs_file_s));
if (priv == NULL)
{
uerr("ERROR: Failed to allocate file attributes\n");
return -ENOMEM;
}
* filep->f_priv.
*/
filep->f_priv = (void *)priv;
return OK;
}
static int arm64_fpu_procfs_close(struct file *filep)
{
struct arm64_fpu_procfs_file_s *priv;
priv = (struct arm64_fpu_procfs_file_s *)filep->f_priv;
DEBUGASSERT(priv);
kmm_free(priv);
filep->f_priv = NULL;
return OK;
}
static ssize_t arm64_fpu_procfs_read(struct file *filep, char *buffer,
size_t buflen)
{
struct arm64_fpu_procfs_file_s *attr;
struct arm64_cpu_fpu_context *ctx;
off_t offset;
int linesize;
int ret;
int i;
uinfo("buffer=%p buflen=%zu\n", buffer, buflen);
attr = (struct arm64_fpu_procfs_file_s *)filep->f_priv;
DEBUGASSERT(attr);
linesize = 0;
for (i = 0; i < CONFIG_SMP_NCPUS; i++)
{
ctx = &per_cpu_var_smp(g_cpu_fpu_ctx, i);
linesize += snprintf(attr->line + linesize,
FPU_PROC_LINELEN,
"CPU%d: save: %d restore: %d "
"switch: %d exedepth: %d\n",
i, ctx->save_count, ctx->restore_count,
ctx->switch_count, ctx->exe_depth_count);
}
attr->linesize = linesize;
offset = filep->f_pos;
ret = procfs_memcpy(attr->line, attr->linesize,
buffer, buflen, &offset);
if (ret > 0)
{
filep->f_pos += ret;
}
return ret;
}
static int arm64_fpu_procfs_stat(const char *relpath, struct stat *buf)
{
buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR;
buf->st_size = 0;
buf->st_blksize = 0;
buf->st_blocks = 0;
return OK;
}
#endif
void arm64_fpu_enable(void)
{
irqstate_t flags = up_irq_save();
arm64_fpu_access_trap_enable();
up_irq_restore(flags);
}
void arm64_fpu_disable(void)
{
irqstate_t flags = up_irq_save();
arm64_fpu_access_trap_disable();
up_irq_restore(flags);
}
* Name: up_fpucmp
*
* Description:
* Compare FPU areas from thread context.
*
* Input Parameters:
* saveregs1 - Pointer to the saved FPU registers.
* saveregs2 - Pointer to the saved FPU registers.
*
* Returned Value:
* True if FPU areas compare equal, False otherwise.
*
***************************************************************************/
bool up_fpucmp(const void *saveregs1, const void *saveregs2)
{
const uint64_t *regs1 = (uint64_t *)((uintptr_t)saveregs1 +
ARM64_CONTEXT_SIZE);
const uint64_t *regs2 = (uint64_t *)((uintptr_t)saveregs2 +
ARM64_CONTEXT_SIZE);
* need to be preserved.
*/
return memcmp(®s1[REG_Q4], ®s2[REG_Q4],
8 * FPU_CALLEE_REGS) == 0;
}
* Name: arm64_fpu_procfs_register
*
* Description:
* Register the arm64 fpu procfs file system entry
*
***************************************************************************/
#ifdef CONFIG_FS_PROCFS_REGISTER
int arm64_fpu_procfs_register(void)
{
return procfs_register(&g_procfs_arm64_fpu);
}
#endif