1210284Sjmallett/***********************license start*************** 2232812Sjmallett * Copyright (c) 2003-2010 Cavium Inc. (support@cavium.com). All rights 3215990Sjmallett * reserved. 4210284Sjmallett * 5210284Sjmallett * 6215990Sjmallett * Redistribution and use in source and binary forms, with or without 7215990Sjmallett * modification, are permitted provided that the following conditions are 8215990Sjmallett * met: 9210284Sjmallett * 10215990Sjmallett * * Redistributions of source code must retain the above copyright 11215990Sjmallett * notice, this list of conditions and the following disclaimer. 12210284Sjmallett * 13215990Sjmallett * * Redistributions in binary form must reproduce the above 14215990Sjmallett * copyright notice, this list of conditions and the following 15215990Sjmallett * disclaimer in the documentation and/or other materials provided 16215990Sjmallett * with the distribution. 17215990Sjmallett 18232812Sjmallett * * Neither the name of Cavium Inc. nor the names of 19215990Sjmallett * its contributors may be used to endorse or promote products 20215990Sjmallett * derived from this software without specific prior written 21215990Sjmallett * permission. 22215990Sjmallett 23215990Sjmallett * This Software, including technical data, may be subject to U.S. export control 24215990Sjmallett * laws, including the U.S. Export Administration Act and its associated 25215990Sjmallett * regulations, and may be subject to export or import regulations in other 26215990Sjmallett * countries. 27215990Sjmallett 28215990Sjmallett * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 29232812Sjmallett * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR 30215990Sjmallett * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO 31215990Sjmallett * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR 32215990Sjmallett * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM 33215990Sjmallett * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, 34215990Sjmallett * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF 35215990Sjmallett * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR 36215990Sjmallett * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR 37215990Sjmallett * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. 38210284Sjmallett ***********************license end**************************************/ 39210284Sjmallett 40210284Sjmallett 41210284Sjmallett 42210284Sjmallett 43210284Sjmallett 44215990Sjmallett 45210284Sjmallett/** 46210284Sjmallett * @file 47210284Sjmallett * Simple executive application initialization for Linux user space. This 48210284Sjmallett * file should be used instead of cvmx-app-init.c for running simple executive 49210284Sjmallett * applications under Linux in userspace. The following are some of the key 50210284Sjmallett * points to remember when writing applications to run both under the 51210284Sjmallett * standalone simple executive and userspace under Linux. 52210284Sjmallett * 53210284Sjmallett * -# Application main must be called "appmain" under Linux. Use and ifdef 54210284Sjmallett * based on __linux__ to determine the proper name. 55210284Sjmallett * -# Be careful to use cvmx_ptr_to_phys() and cvmx_phys_to_ptr. The simple 56210284Sjmallett * executive 1-1 TLB mappings allow you to be sloppy and interchange 57210284Sjmallett * hardware addresses with virtual address. This isn't true under Linux. 58210284Sjmallett * -# If you're talking directly to hardware, be careful. The normal Linux 59210284Sjmallett * protections are circumvented. If you do something bad, Linux won't 60210284Sjmallett * save you. 61210284Sjmallett * -# Most hardware can only be initialized once. Unless you're very careful, 62210284Sjmallett * this also means you Linux application can only run once. 63210284Sjmallett * 64232812Sjmallett * <hr>$Revision: 70129 $<hr> 65210284Sjmallett * 66210284Sjmallett */ 67210284Sjmallett#define _GNU_SOURCE 68210284Sjmallett#include <stdint.h> 69210284Sjmallett#include <stdio.h> 70210284Sjmallett#include <stdlib.h> 71210284Sjmallett#include <stdarg.h> 72210284Sjmallett#include <string.h> 73210284Sjmallett#include <unistd.h> 74210284Sjmallett#include <errno.h> 75210284Sjmallett#include <fcntl.h> 76210284Sjmallett#include <sys/mman.h> 77210284Sjmallett#include <signal.h> 78210284Sjmallett#include <sys/statfs.h> 79210284Sjmallett#include <sys/wait.h> 80210284Sjmallett#include <sys/sysmips.h> 81210284Sjmallett#include <sched.h> 82210284Sjmallett#include <octeon-app-init.h> 83210284Sjmallett 84210284Sjmallett#include "cvmx-config.h" 85210284Sjmallett#include "cvmx.h" 86210284Sjmallett#include "cvmx-atomic.h" 87210284Sjmallett#include "cvmx-sysinfo.h" 88210284Sjmallett#include "cvmx-coremask.h" 89210284Sjmallett#include "cvmx-spinlock.h" 90210284Sjmallett#include "cvmx-bootmem.h" 91232812Sjmallett#include "cvmx-helper-cfg.h" 92210284Sjmallett 93210284Sjmallettint octeon_model_version_check(uint32_t chip_id); 94210284Sjmallett 95210284Sjmallett#define OCTEON_ECLOCK_MULT_INPUT_X16 ((int)(33.4*16)) 96210284Sjmallett 97210284Sjmallett/* Applications using the simple executive libraries under Linux userspace must 98210284Sjmallett rename their "main" function to match the prototype below. This allows the 99210284Sjmallett simple executive to perform needed memory initialization and process 100210284Sjmallett creation before the application runs. */ 101210284Sjmallettextern int appmain(int argc, const char *argv[]); 102210284Sjmallett 103210284Sjmallett/* These two external addresses provide the beginning and end markers for the 104210284Sjmallett CVMX_SHARED section. These are defined by the cvmx-shared.ld linker script. 105210284Sjmallett If they aren't defined, you probably forgot to link using this script. */ 106210284Sjmallettextern void __cvmx_shared_start; 107210284Sjmallettextern void __cvmx_shared_end; 108210284Sjmallettextern uint64_t linux_mem32_min; 109210284Sjmallettextern uint64_t linux_mem32_max; 110210284Sjmallettextern uint64_t linux_mem32_wired; 111210284Sjmallettextern uint64_t linux_mem32_offset; 112210284Sjmallett 113210284Sjmallett/** 114210284Sjmallett * This function performs some default initialization of the Octeon executive. It initializes 115210284Sjmallett * the cvmx_bootmem memory allocator with the list of physical memory shared by the bootloader. 116210284Sjmallett * This function should be called on all cores that will use the bootmem allocator. 117210284Sjmallett * Applications which require a different configuration can replace this function with a suitable application 118210284Sjmallett * specific one. 119210284Sjmallett * 120210284Sjmallett * @return 0 on success 121210284Sjmallett * -1 on failure 122210284Sjmallett */ 123210284Sjmallettint cvmx_user_app_init(void) 124210284Sjmallett{ 125210284Sjmallett return 0; 126210284Sjmallett} 127210284Sjmallett 128210284Sjmallett 129210284Sjmallett/** 130210284Sjmallett * Simulator magic is not supported in user mode under Linux. 131210284Sjmallett * This version of simprintf simply calls the underlying C 132210284Sjmallett * library printf for output. It also makes sure that two 133210284Sjmallett * calls to simprintf provide atomic output. 134210284Sjmallett * 135215990Sjmallett * @param format Format string in the same format as printf. 136210284Sjmallett */ 137215990Sjmallettvoid simprintf(const char *format, ...) 138210284Sjmallett{ 139210284Sjmallett CVMX_SHARED static cvmx_spinlock_t simprintf_lock = CVMX_SPINLOCK_UNLOCKED_INITIALIZER; 140210284Sjmallett va_list ap; 141210284Sjmallett 142210284Sjmallett cvmx_spinlock_lock(&simprintf_lock); 143210284Sjmallett printf("SIMPRINTF(%d): ", (int)cvmx_get_core_num()); 144215990Sjmallett va_start(ap, format); 145215990Sjmallett vprintf(format, ap); 146210284Sjmallett va_end(ap); 147210284Sjmallett cvmx_spinlock_unlock(&simprintf_lock); 148210284Sjmallett} 149210284Sjmallett 150210284Sjmallett 151210284Sjmallett/** 152210284Sjmallett * Setup the CVMX_SHARED data section to be shared across 153210284Sjmallett * all processors running this application. A memory mapped 154210284Sjmallett * region is allocated using shm_open and mmap. The current 155210284Sjmallett * contents of the CVMX_SHARED section are copied into the 156210284Sjmallett * region. Then the new region is remapped to replace the 157210284Sjmallett * existing CVMX_SHARED data. 158210284Sjmallett * 159210284Sjmallett * This function will display a message and abort the 160210284Sjmallett * application under any error conditions. The Linux tmpfs 161210284Sjmallett * filesystem must be mounted under /dev/shm. 162210284Sjmallett */ 163210284Sjmallettstatic void setup_cvmx_shared(void) 164210284Sjmallett{ 165210284Sjmallett const char *SHM_NAME = "cvmx_shared"; 166210284Sjmallett unsigned long shared_size = &__cvmx_shared_end - &__cvmx_shared_start; 167210284Sjmallett int fd; 168210284Sjmallett 169210284Sjmallett /* If there isn't and shared data we can skip all this */ 170210284Sjmallett if (shared_size) 171210284Sjmallett { 172210284Sjmallett char shm_name[30]; 173210284Sjmallett printf("CVMX_SHARED: %p-%p\n", &__cvmx_shared_start, &__cvmx_shared_end); 174210284Sjmallett 175210284Sjmallett#ifdef __UCLIBC__ 176210284Sjmallett const char *defaultdir = "/dev/shm/"; 177210284Sjmallett struct statfs f; 178210284Sjmallett int pid; 179210284Sjmallett /* The canonical place is /dev/shm. */ 180210284Sjmallett if (statfs (defaultdir, &f) == 0) 181210284Sjmallett { 182210284Sjmallett pid = getpid(); 183210284Sjmallett sprintf (shm_name, "%s%s-%d", defaultdir, SHM_NAME, pid); 184210284Sjmallett } 185210284Sjmallett else 186210284Sjmallett { 187210284Sjmallett perror("/dev/shm is not mounted"); 188210284Sjmallett exit(-1); 189210284Sjmallett } 190210284Sjmallett 191210284Sjmallett /* shm_open(), shm_unlink() are not implemented in uClibc. Do the 192210284Sjmallett same thing using open() and close() system calls. */ 193210284Sjmallett fd = open (shm_name, O_RDWR | O_CREAT | O_TRUNC, 0); 194210284Sjmallett 195210284Sjmallett if (fd < 0) 196210284Sjmallett { 197210284Sjmallett perror("Failed to open CVMX_SHARED(shm_name)"); 198210284Sjmallett exit(errno); 199210284Sjmallett } 200210284Sjmallett 201210284Sjmallett unlink (shm_name); 202210284Sjmallett#else 203210284Sjmallett sprintf(shm_name, "%s-%d", SHM_NAME, getpid()); 204210284Sjmallett /* Open a new shared memory region for use as CVMX_SHARED */ 205210284Sjmallett fd = shm_open(shm_name, O_RDWR | O_CREAT | O_TRUNC, 0); 206210284Sjmallett if (fd <0) 207210284Sjmallett { 208210284Sjmallett perror("Failed to setup CVMX_SHARED(shm_open)"); 209210284Sjmallett exit(errno); 210210284Sjmallett } 211210284Sjmallett 212210284Sjmallett /* We don't want the file on the filesystem. Immediately unlink it so 213210284Sjmallett another application can create its own shared region */ 214210284Sjmallett shm_unlink(shm_name); 215210284Sjmallett#endif 216210284Sjmallett 217210284Sjmallett /* Resize the region to match the size of CVMX_SHARED */ 218210284Sjmallett ftruncate(fd, shared_size); 219210284Sjmallett 220210284Sjmallett /* Map the region into some random location temporarily so we can 221210284Sjmallett copy the shared data to it */ 222210284Sjmallett void *ptr = mmap(NULL, shared_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 223210284Sjmallett if (ptr == NULL) 224210284Sjmallett { 225210284Sjmallett perror("Failed to setup CVMX_SHARED(mmap copy)"); 226210284Sjmallett exit(errno); 227210284Sjmallett } 228210284Sjmallett 229210284Sjmallett /* Copy CVMX_SHARED to the new shared region so we don't lose 230210284Sjmallett initializers */ 231210284Sjmallett memcpy(ptr, &__cvmx_shared_start, shared_size); 232210284Sjmallett munmap(ptr, shared_size); 233210284Sjmallett 234210284Sjmallett /* Remap the shared region to replace the old CVMX_SHARED region */ 235210284Sjmallett ptr = mmap(&__cvmx_shared_start, shared_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); 236210284Sjmallett if (ptr == NULL) 237210284Sjmallett { 238210284Sjmallett perror("Failed to setup CVMX_SHARED(mmap final)"); 239210284Sjmallett exit(errno); 240210284Sjmallett } 241210284Sjmallett 242210284Sjmallett /* Once mappings are setup, the file handle isn't needed anymore */ 243210284Sjmallett close(fd); 244210284Sjmallett } 245210284Sjmallett} 246210284Sjmallett 247210284Sjmallett 248210284Sjmallett/** 249210284Sjmallett * Shutdown and free the shared CVMX_SHARED region setup by 250210284Sjmallett * setup_cvmx_shared. 251210284Sjmallett */ 252210284Sjmallettstatic void shutdown_cvmx_shared(void) 253210284Sjmallett{ 254210284Sjmallett unsigned long shared_size = &__cvmx_shared_end - &__cvmx_shared_start; 255210284Sjmallett if (shared_size) 256210284Sjmallett munmap(&__cvmx_shared_start, shared_size); 257210284Sjmallett} 258210284Sjmallett 259210284Sjmallett 260210284Sjmallett/** 261210284Sjmallett * Setup access to the CONFIG_CAVIUM_RESERVE32 memory section 262210284Sjmallett * created by the kernel. This memory is used for shared 263210284Sjmallett * hardware buffers with 32 bit userspace applications. 264210284Sjmallett */ 265210284Sjmallettstatic void setup_reserve32(void) 266210284Sjmallett{ 267210284Sjmallett if (linux_mem32_min && linux_mem32_max) 268210284Sjmallett { 269210284Sjmallett int region_size = linux_mem32_max - linux_mem32_min + 1; 270210284Sjmallett int mmap_flags = MAP_SHARED; 271210284Sjmallett void *linux_mem32_base_ptr = NULL; 272210284Sjmallett 273210284Sjmallett /* Although not strictly necessary, we are going to mmap() the wired 274210284Sjmallett TLB region so it is in the process page tables. These pages will 275210284Sjmallett never fault in, but they will allow GDB to access the wired 276210284Sjmallett region. We need the mappings to exactly match the wired TLB 277210284Sjmallett entry. */ 278210284Sjmallett if (linux_mem32_wired) 279210284Sjmallett { 280210284Sjmallett mmap_flags |= MAP_FIXED; 281210284Sjmallett linux_mem32_base_ptr = CASTPTR(void, (1ull<<31) - region_size); 282210284Sjmallett } 283210284Sjmallett 284210284Sjmallett int fd = open("/dev/mem", O_RDWR); 285210284Sjmallett if (fd < 0) 286210284Sjmallett { 287210284Sjmallett perror("ERROR opening /dev/mem"); 288210284Sjmallett exit(-1); 289210284Sjmallett } 290210284Sjmallett 291210284Sjmallett linux_mem32_base_ptr = mmap64(linux_mem32_base_ptr, 292210284Sjmallett region_size, 293210284Sjmallett PROT_READ | PROT_WRITE, 294210284Sjmallett mmap_flags, 295210284Sjmallett fd, 296210284Sjmallett linux_mem32_min); 297210284Sjmallett close(fd); 298210284Sjmallett 299210284Sjmallett if (MAP_FAILED == linux_mem32_base_ptr) 300210284Sjmallett { 301210284Sjmallett perror("Error mapping reserve32"); 302210284Sjmallett exit(-1); 303210284Sjmallett } 304210284Sjmallett 305210284Sjmallett linux_mem32_offset = CAST64(linux_mem32_base_ptr) - linux_mem32_min; 306210284Sjmallett } 307210284Sjmallett} 308210284Sjmallett 309210284Sjmallett 310210284Sjmallett/** 311210284Sjmallett * Main entrypoint of the application. Here we setup shared 312210284Sjmallett * memory and fork processes for each cpu. This simulates the 313210284Sjmallett * normal simple executive environment of one process per 314210284Sjmallett * cpu core. 315210284Sjmallett * 316210284Sjmallett * @param argc Number of command line arguments 317210284Sjmallett * @param argv The command line arguments 318210284Sjmallett * @return Return value for the process 319210284Sjmallett */ 320210284Sjmallettint main(int argc, const char *argv[]) 321210284Sjmallett{ 322210284Sjmallett CVMX_SHARED static cvmx_spinlock_t mask_lock = CVMX_SPINLOCK_UNLOCKED_INITIALIZER; 323210284Sjmallett CVMX_SHARED static int32_t pending_fork; 324210284Sjmallett unsigned long cpumask; 325210284Sjmallett unsigned long cpu; 326215990Sjmallett int firstcpu = 0; 327215990Sjmallett int firstcore = 0; 328210284Sjmallett 329215990Sjmallett cvmx_linux_enable_xkphys_access(0); 330210284Sjmallett cvmx_sysinfo_linux_userspace_initialize(); 331210284Sjmallett 332210284Sjmallett if (sizeof(void*) == 4) 333210284Sjmallett { 334210284Sjmallett if (linux_mem32_min) 335210284Sjmallett setup_reserve32(); 336210284Sjmallett else 337210284Sjmallett { 338210284Sjmallett printf("\nFailed to access 32bit shared memory region. Most likely the Kernel\n" 339210284Sjmallett "has not been configured for 32bit shared memory access. Check the\n" 340210284Sjmallett "kernel configuration.\n" 341210284Sjmallett "Aborting...\n\n"); 342210284Sjmallett exit(-1); 343210284Sjmallett } 344210284Sjmallett } 345210284Sjmallett 346210284Sjmallett setup_cvmx_shared(); 347215990Sjmallett cvmx_bootmem_init(cvmx_sysinfo_get()->phy_mem_desc_addr); 348210284Sjmallett 349210284Sjmallett /* Check to make sure the Chip version matches the configured version */ 350210284Sjmallett octeon_model_version_check(cvmx_get_proc_id()); 351210284Sjmallett 352232812Sjmallett /* Initialize configuration to set bpid, pkind, pko_port for all the 353232812Sjmallett available ports connected. */ 354232812Sjmallett __cvmx_helper_cfg_init(); 355232812Sjmallett 356210284Sjmallett /* Get the list of logical cpus we should run on */ 357210284Sjmallett if (sched_getaffinity(0, sizeof(cpumask), (cpu_set_t*)&cpumask)) 358210284Sjmallett { 359210284Sjmallett perror("sched_getaffinity failed"); 360210284Sjmallett exit(errno); 361210284Sjmallett } 362210284Sjmallett 363210284Sjmallett cvmx_sysinfo_t *system_info = cvmx_sysinfo_get(); 364210284Sjmallett 365210284Sjmallett cvmx_atomic_set32(&pending_fork, 1); 366215990Sjmallett 367215990Sjmallett /* Get the lowest logical cpu */ 368215990Sjmallett firstcore = ffsl(cpumask) - 1; 369232812Sjmallett cpumask ^= (1ull<<(firstcore)); 370215990Sjmallett while (1) 371210284Sjmallett { 372215990Sjmallett if (cpumask == 0) 373210284Sjmallett { 374215990Sjmallett cpu = firstcore; 375215990Sjmallett firstcpu = 1; 376215990Sjmallett break; 377210284Sjmallett } 378215990Sjmallett cpu = ffsl(cpumask) - 1; 379215990Sjmallett /* Turn off the bit for this CPU number. We've counted him */ 380232812Sjmallett cpumask ^= (1ull<<cpu); 381215990Sjmallett /* Increment the number of CPUs running this app */ 382232812Sjmallett cvmx_atomic_add32(&pending_fork, 1); 383215990Sjmallett /* Flush all IO streams before the fork. Otherwise any buffered 384215990Sjmallett data in the C library will be duplicated. This results in 385215990Sjmallett duplicate output from a single print */ 386215990Sjmallett fflush(NULL); 387215990Sjmallett /* Fork a process for the new CPU */ 388215990Sjmallett int pid = fork(); 389215990Sjmallett if (pid == 0) 390215990Sjmallett { 391215990Sjmallett break; 392215990Sjmallett } 393215990Sjmallett else if (pid == -1) 394215990Sjmallett { 395215990Sjmallett perror("Fork failed"); 396215990Sjmallett exit(errno); 397215990Sjmallett } 398215990Sjmallett } 399210284Sjmallett 400215990Sjmallett 401210284Sjmallett /* Set affinity to lock me to the correct CPU */ 402210284Sjmallett cpumask = (1<<cpu); 403210284Sjmallett if (sched_setaffinity(0, sizeof(cpumask), (cpu_set_t*)&cpumask)) 404210284Sjmallett { 405210284Sjmallett perror("sched_setaffinity failed"); 406210284Sjmallett exit(errno); 407210284Sjmallett } 408210284Sjmallett 409210284Sjmallett cvmx_spinlock_lock(&mask_lock); 410210284Sjmallett system_info->core_mask |= 1<<cvmx_get_core_num(); 411210284Sjmallett cvmx_atomic_add32(&pending_fork, -1); 412210284Sjmallett if (cvmx_atomic_get32(&pending_fork) == 0) 413232812Sjmallett { 414210284Sjmallett cvmx_dprintf("Active coremask = 0x%x\n", system_info->core_mask); 415232812Sjmallett } 416215990Sjmallett if (firstcpu) 417210284Sjmallett system_info->init_core = cvmx_get_core_num(); 418210284Sjmallett cvmx_spinlock_unlock(&mask_lock); 419210284Sjmallett 420210284Sjmallett /* Spinning waiting for forks to complete */ 421210284Sjmallett while (cvmx_atomic_get32(&pending_fork)) {} 422210284Sjmallett 423210284Sjmallett cvmx_coremask_barrier_sync(system_info->core_mask); 424210284Sjmallett 425215990Sjmallett cvmx_linux_enable_xkphys_access(1); 426210284Sjmallett 427210284Sjmallett int result = appmain(argc, argv); 428210284Sjmallett 429210284Sjmallett /* Wait for all forks to complete. This needs to be the core that started 430210284Sjmallett all of the forks. It may not be the lowest numbered core! */ 431210284Sjmallett if (cvmx_get_core_num() == system_info->init_core) 432210284Sjmallett { 433210284Sjmallett int num_waits; 434210284Sjmallett CVMX_POP(num_waits, system_info->core_mask); 435210284Sjmallett num_waits--; 436210284Sjmallett while (num_waits--) 437210284Sjmallett { 438210284Sjmallett if (wait(NULL) == -1) 439210284Sjmallett perror("CVMX: Wait for forked child failed\n"); 440210284Sjmallett } 441210284Sjmallett } 442210284Sjmallett 443210284Sjmallett shutdown_cvmx_shared(); 444210284Sjmallett 445210284Sjmallett return result; 446210284Sjmallett} 447