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