diff options
author | Juli Mallett <jmallett@FreeBSD.org> | 2010-07-20 07:11:19 +0000 |
---|---|---|
committer | Juli Mallett <jmallett@FreeBSD.org> | 2010-07-20 07:11:19 +0000 |
commit | 1c305b501145f696d3597fb9b5b2091caaa6f67c (patch) | |
tree | 776ea14a76df76cd5ee4d9b63107c1e819c68914 /cvmx-app-init-linux.c |
Initial import of Cavium Networks Octeon Simple Executive, SDK version 1.9.0.vendor/octeon-sdk/1.9.0
Notes
Notes:
svn path=/vendor-sys/octeon-sdk/dist/; revision=210284
svn path=/vendor-sys/octeon-sdk/1.9.0/; revision=210285; tag=vendor/octeon-sdk/1.9.0
Diffstat (limited to 'cvmx-app-init-linux.c')
-rw-r--r-- | cvmx-app-init-linux.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/cvmx-app-init-linux.c b/cvmx-app-init-linux.c new file mode 100644 index 000000000000..ed83b50dbe78 --- /dev/null +++ b/cvmx-app-init-linux.c @@ -0,0 +1,457 @@ +/***********************license start*************** + * Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights + * reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * Neither the name of Cavium Networks nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" + * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS + * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH + * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY + * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT + * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES + * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR + * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET + * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT + * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU. + * + * + * For any questions regarding licensing please contact marketing@caviumnetworks.com + * + ***********************license end**************************************/ + + + + + +/** + * @file + * Simple executive application initialization for Linux user space. This + * file should be used instead of cvmx-app-init.c for running simple executive + * applications under Linux in userspace. The following are some of the key + * points to remember when writing applications to run both under the + * standalone simple executive and userspace under Linux. + * + * -# Application main must be called "appmain" under Linux. Use and ifdef + * based on __linux__ to determine the proper name. + * -# Be careful to use cvmx_ptr_to_phys() and cvmx_phys_to_ptr. The simple + * executive 1-1 TLB mappings allow you to be sloppy and interchange + * hardware addresses with virtual address. This isn't true under Linux. + * -# If you're talking directly to hardware, be careful. The normal Linux + * protections are circumvented. If you do something bad, Linux won't + * save you. + * -# Most hardware can only be initialized once. Unless you're very careful, + * this also means you Linux application can only run once. + * + * <hr>$Revision: 41757 $<hr> + * + */ +#define _GNU_SOURCE +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <signal.h> +#include <sys/statfs.h> +#include <sys/wait.h> +#include <sys/sysmips.h> +#include <sched.h> +#include <octeon-app-init.h> + +#include "cvmx-config.h" +#include "cvmx.h" +#include "cvmx-atomic.h" +#include "cvmx-sysinfo.h" +#include "cvmx-coremask.h" +#include "cvmx-spinlock.h" +#include "cvmx-bootmem.h" + +int octeon_model_version_check(uint32_t chip_id); + +#define OCTEON_ECLOCK_MULT_INPUT_X16 ((int)(33.4*16)) + +/* Applications using the simple executive libraries under Linux userspace must + rename their "main" function to match the prototype below. This allows the + simple executive to perform needed memory initialization and process + creation before the application runs. */ +extern int appmain(int argc, const char *argv[]); + +/* These two external addresses provide the beginning and end markers for the + CVMX_SHARED section. These are defined by the cvmx-shared.ld linker script. + If they aren't defined, you probably forgot to link using this script. */ +extern void __cvmx_shared_start; +extern void __cvmx_shared_end; +extern uint64_t linux_mem32_min; +extern uint64_t linux_mem32_max; +extern uint64_t linux_mem32_wired; +extern uint64_t linux_mem32_offset; + +#define MIPS_CAVIUM_XKPHYS_READ 2010 /* XKPHYS */ +#define MIPS_CAVIUM_XKPHYS_WRITE 2011 /* XKPHYS */ + +static CVMX_SHARED int32_t warn_count; + +/** + * This function performs some default initialization of the Octeon executive. It initializes + * the cvmx_bootmem memory allocator with the list of physical memory shared by the bootloader. + * This function should be called on all cores that will use the bootmem allocator. + * Applications which require a different configuration can replace this function with a suitable application + * specific one. + * + * @return 0 on success + * -1 on failure + */ +int cvmx_user_app_init(void) +{ + return 0; +} + + +/** + * Simulator magic is not supported in user mode under Linux. + * This version of simprintf simply calls the underlying C + * library printf for output. It also makes sure that two + * calls to simprintf provide atomic output. + * + * @param fmt Format string in the same format as printf. + */ +void simprintf(const char *fmt, ...) +{ + CVMX_SHARED static cvmx_spinlock_t simprintf_lock = CVMX_SPINLOCK_UNLOCKED_INITIALIZER; + va_list ap; + + cvmx_spinlock_lock(&simprintf_lock); + printf("SIMPRINTF(%d): ", (int)cvmx_get_core_num()); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + cvmx_spinlock_unlock(&simprintf_lock); +} + + +/** + * Setup the CVMX_SHARED data section to be shared across + * all processors running this application. A memory mapped + * region is allocated using shm_open and mmap. The current + * contents of the CVMX_SHARED section are copied into the + * region. Then the new region is remapped to replace the + * existing CVMX_SHARED data. + * + * This function will display a message and abort the + * application under any error conditions. The Linux tmpfs + * filesystem must be mounted under /dev/shm. + */ +static void setup_cvmx_shared(void) +{ + const char *SHM_NAME = "cvmx_shared"; + unsigned long shared_size = &__cvmx_shared_end - &__cvmx_shared_start; + int fd; + + /* If there isn't and shared data we can skip all this */ + if (shared_size) + { + char shm_name[30]; + printf("CVMX_SHARED: %p-%p\n", &__cvmx_shared_start, &__cvmx_shared_end); + +#ifdef __UCLIBC__ + const char *defaultdir = "/dev/shm/"; + struct statfs f; + int pid; + /* The canonical place is /dev/shm. */ + if (statfs (defaultdir, &f) == 0) + { + pid = getpid(); + sprintf (shm_name, "%s%s-%d", defaultdir, SHM_NAME, pid); + } + else + { + perror("/dev/shm is not mounted"); + exit(-1); + } + + /* shm_open(), shm_unlink() are not implemented in uClibc. Do the + same thing using open() and close() system calls. */ + fd = open (shm_name, O_RDWR | O_CREAT | O_TRUNC, 0); + + if (fd < 0) + { + perror("Failed to open CVMX_SHARED(shm_name)"); + exit(errno); + } + + unlink (shm_name); +#else + sprintf(shm_name, "%s-%d", SHM_NAME, getpid()); + /* Open a new shared memory region for use as CVMX_SHARED */ + fd = shm_open(shm_name, O_RDWR | O_CREAT | O_TRUNC, 0); + if (fd <0) + { + perror("Failed to setup CVMX_SHARED(shm_open)"); + exit(errno); + } + + /* We don't want the file on the filesystem. Immediately unlink it so + another application can create its own shared region */ + shm_unlink(shm_name); +#endif + + /* Resize the region to match the size of CVMX_SHARED */ + ftruncate(fd, shared_size); + + /* Map the region into some random location temporarily so we can + copy the shared data to it */ + void *ptr = mmap(NULL, shared_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (ptr == NULL) + { + perror("Failed to setup CVMX_SHARED(mmap copy)"); + exit(errno); + } + + /* Copy CVMX_SHARED to the new shared region so we don't lose + initializers */ + memcpy(ptr, &__cvmx_shared_start, shared_size); + munmap(ptr, shared_size); + + /* Remap the shared region to replace the old CVMX_SHARED region */ + ptr = mmap(&__cvmx_shared_start, shared_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); + if (ptr == NULL) + { + perror("Failed to setup CVMX_SHARED(mmap final)"); + exit(errno); + } + + /* Once mappings are setup, the file handle isn't needed anymore */ + close(fd); + } +} + + +/** + * Shutdown and free the shared CVMX_SHARED region setup by + * setup_cvmx_shared. + */ +static void shutdown_cvmx_shared(void) +{ + unsigned long shared_size = &__cvmx_shared_end - &__cvmx_shared_start; + if (shared_size) + munmap(&__cvmx_shared_start, shared_size); +} + + +/** + * Setup access to the CONFIG_CAVIUM_RESERVE32 memory section + * created by the kernel. This memory is used for shared + * hardware buffers with 32 bit userspace applications. + */ +static void setup_reserve32(void) +{ + if (linux_mem32_min && linux_mem32_max) + { + int region_size = linux_mem32_max - linux_mem32_min + 1; + int mmap_flags = MAP_SHARED; + void *linux_mem32_base_ptr = NULL; + + /* Although not strictly necessary, we are going to mmap() the wired + TLB region so it is in the process page tables. These pages will + never fault in, but they will allow GDB to access the wired + region. We need the mappings to exactly match the wired TLB + entry. */ + if (linux_mem32_wired) + { + mmap_flags |= MAP_FIXED; + linux_mem32_base_ptr = CASTPTR(void, (1ull<<31) - region_size); + } + + int fd = open("/dev/mem", O_RDWR); + if (fd < 0) + { + perror("ERROR opening /dev/mem"); + exit(-1); + } + + linux_mem32_base_ptr = mmap64(linux_mem32_base_ptr, + region_size, + PROT_READ | PROT_WRITE, + mmap_flags, + fd, + linux_mem32_min); + close(fd); + + if (MAP_FAILED == linux_mem32_base_ptr) + { + perror("Error mapping reserve32"); + exit(-1); + } + + linux_mem32_offset = CAST64(linux_mem32_base_ptr) - linux_mem32_min; + } +} + + +/** + * Main entrypoint of the application. Here we setup shared + * memory and fork processes for each cpu. This simulates the + * normal simple executive environment of one process per + * cpu core. + * + * @param argc Number of command line arguments + * @param argv The command line arguments + * @return Return value for the process + */ +int main(int argc, const char *argv[]) +{ + CVMX_SHARED static cvmx_spinlock_t mask_lock = CVMX_SPINLOCK_UNLOCKED_INITIALIZER; + CVMX_SHARED static int32_t pending_fork; + unsigned long cpumask; + unsigned long cpu; + int lastcpu = 0; + + cvmx_sysinfo_linux_userspace_initialize(); + + if (sizeof(void*) == 4) + { + if (linux_mem32_min) + setup_reserve32(); + else + { + printf("\nFailed to access 32bit shared memory region. Most likely the Kernel\n" + "has not been configured for 32bit shared memory access. Check the\n" + "kernel configuration.\n" + "Aborting...\n\n"); + exit(-1); + } + } + + setup_cvmx_shared(); + cvmx_bootmem_init(cvmx_sysinfo_get()->phy_mem_desc_ptr); + + /* Check to make sure the Chip version matches the configured version */ + octeon_model_version_check(cvmx_get_proc_id()); + + /* Get the list of logical cpus we should run on */ + if (sched_getaffinity(0, sizeof(cpumask), (cpu_set_t*)&cpumask)) + { + perror("sched_getaffinity failed"); + exit(errno); + } + + cvmx_sysinfo_t *system_info = cvmx_sysinfo_get(); + + cvmx_atomic_set32(&pending_fork, 1); + for (cpu=0; cpu<16; cpu++) + { + if (cpumask & (1<<cpu)) + { + /* Turn off the bit for this CPU number. We've counted him */ + cpumask ^= (1<<cpu); + /* If this is the last CPU to run on, use this process instead of forking another one */ + if (cpumask == 0) + { + lastcpu = 1; + break; + } + /* Increment the number of CPUs running this app */ + cvmx_atomic_add32(&pending_fork, 1); + /* Flush all IO streams before the fork. Otherwise any buffered + data in the C library will be duplicated. This results in + duplicate output from a single print */ + fflush(NULL); + /* Fork a process for the new CPU */ + int pid = fork(); + if (pid == 0) + { + break; + } + else if (pid == -1) + { + perror("Fork failed"); + exit(errno); + } + } + } + + /* Set affinity to lock me to the correct CPU */ + cpumask = (1<<cpu); + if (sched_setaffinity(0, sizeof(cpumask), (cpu_set_t*)&cpumask)) + { + perror("sched_setaffinity failed"); + exit(errno); + } + + cvmx_spinlock_lock(&mask_lock); + system_info->core_mask |= 1<<cvmx_get_core_num(); + cvmx_atomic_add32(&pending_fork, -1); + if (cvmx_atomic_get32(&pending_fork) == 0) + cvmx_dprintf("Active coremask = 0x%x\n", system_info->core_mask); + if (lastcpu) + system_info->init_core = cvmx_get_core_num(); + cvmx_spinlock_unlock(&mask_lock); + + /* Spinning waiting for forks to complete */ + while (cvmx_atomic_get32(&pending_fork)) {} + + cvmx_coremask_barrier_sync(system_info->core_mask); + + int ret = sysmips(MIPS_CAVIUM_XKPHYS_WRITE, getpid(), 3, 0); + if (ret != 0) { + int32_t w = cvmx_atomic_fetch_and_add32(&warn_count, 1); + if (!w) { + switch(errno) { + case EINVAL: + perror("sysmips(MIPS_CAVIUM_XKPHYS_WRITE) failed.\n" + " Did you configure your kernel with both:\n" + " CONFIG_CAVIUM_OCTEON_USER_MEM_PER_PROCESS *and*\n" + " CONFIG_CAVIUM_OCTEON_USER_IO_PER_PROCESS?"); + break; + case EPERM: + perror("sysmips(MIPS_CAVIUM_XKPHYS_WRITE) failed.\n" + " Are you running as root?"); + break; + default: + perror("sysmips(MIPS_CAVIUM_XKPHYS_WRITE) failed"); + break; + } + } + } + + int result = appmain(argc, argv); + + /* Wait for all forks to complete. This needs to be the core that started + all of the forks. It may not be the lowest numbered core! */ + if (cvmx_get_core_num() == system_info->init_core) + { + int num_waits; + CVMX_POP(num_waits, system_info->core_mask); + num_waits--; + while (num_waits--) + { + if (wait(NULL) == -1) + perror("CVMX: Wait for forked child failed\n"); + } + } + + shutdown_cvmx_shared(); + + return result; +} |