1/***********************license start***************
2 * Copyright (c) 2003-2010  Cavium Inc. (support@cavium.com). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *   * Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *
13 *   * Redistributions in binary form must reproduce the above
14 *     copyright notice, this list of conditions and the following
15 *     disclaimer in the documentation and/or other materials provided
16 *     with the distribution.
17
18 *   * Neither the name of Cavium Inc. nor the names of
19 *     its contributors may be used to endorse or promote products
20 *     derived from this software without specific prior written
21 *     permission.
22
23 * This Software, including technical data, may be subject to U.S. export  control
24 * laws, including the U.S. Export Administration Act and its  associated
25 * regulations, and may be subject to export or import  regulations in other
26 * countries.
27
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39
40
41
42
43
44
45/**
46 * @file
47 * Simple executive application initialization for Linux user space. This
48 * file should be used instead of cvmx-app-init.c for running simple executive
49 * applications under Linux in userspace. The following are some of the key
50 * points to remember when writing applications to run both under the
51 * standalone simple executive and userspace under Linux.
52 *
53 * -# Application main must be called "appmain" under Linux. Use and ifdef
54 *      based on __linux__ to determine the proper name.
55 * -# Be careful to use cvmx_ptr_to_phys() and cvmx_phys_to_ptr. The simple
56 *      executive 1-1 TLB mappings allow you to be sloppy and interchange
57 *      hardware addresses with virtual address. This isn't true under Linux.
58 * -# If you're talking directly to hardware, be careful. The normal Linux
59 *      protections are circumvented. If you do something bad, Linux won't
60 *      save you.
61 * -# Most hardware can only be initialized once. Unless you're very careful,
62 *      this also means you Linux application can only run once.
63 *
64 * <hr>$Revision: 70129 $<hr>
65 *
66 */
67#define _GNU_SOURCE
68#include <stdint.h>
69#include <stdio.h>
70#include <stdlib.h>
71#include <stdarg.h>
72#include <string.h>
73#include <unistd.h>
74#include <errno.h>
75#include <fcntl.h>
76#include <sys/mman.h>
77#include <signal.h>
78#include <sys/statfs.h>
79#include <sys/wait.h>
80#include <sys/sysmips.h>
81#include <sched.h>
82#include <octeon-app-init.h>
83
84#include "cvmx-config.h"
85#include "cvmx.h"
86#include "cvmx-atomic.h"
87#include "cvmx-sysinfo.h"
88#include "cvmx-coremask.h"
89#include "cvmx-spinlock.h"
90#include "cvmx-bootmem.h"
91#include "cvmx-helper-cfg.h"
92
93int octeon_model_version_check(uint32_t chip_id);
94
95#define OCTEON_ECLOCK_MULT_INPUT_X16    ((int)(33.4*16))
96
97/* Applications using the simple executive libraries under Linux userspace must
98    rename their "main" function to match the prototype below. This allows the
99    simple executive to perform needed memory initialization and process
100    creation before the application runs. */
101extern int appmain(int argc, const char *argv[]);
102
103/* These two external addresses provide the beginning and end markers for the
104    CVMX_SHARED section. These are defined by the cvmx-shared.ld linker script.
105    If they aren't defined, you probably forgot to link using this script. */
106extern void __cvmx_shared_start;
107extern void __cvmx_shared_end;
108extern uint64_t linux_mem32_min;
109extern uint64_t linux_mem32_max;
110extern uint64_t linux_mem32_wired;
111extern uint64_t linux_mem32_offset;
112
113/**
114 * This function performs some default initialization of the Octeon executive.  It initializes
115 * the cvmx_bootmem memory allocator with the list of physical memory shared by the bootloader.
116 * This function should be called on all cores that will use the bootmem allocator.
117 * Applications which require a different configuration can replace this function with a suitable application
118 * specific one.
119 *
120 * @return 0 on success
121 *         -1 on failure
122 */
123int cvmx_user_app_init(void)
124{
125    return 0;
126}
127
128
129/**
130 * Simulator magic is not supported in user mode under Linux.
131 * This version of simprintf simply calls the underlying C
132 * library printf for output. It also makes sure that two
133 * calls to simprintf provide atomic output.
134 *
135 * @param format  Format string in the same format as printf.
136 */
137void simprintf(const char *format, ...)
138{
139    CVMX_SHARED static cvmx_spinlock_t simprintf_lock = CVMX_SPINLOCK_UNLOCKED_INITIALIZER;
140    va_list ap;
141
142    cvmx_spinlock_lock(&simprintf_lock);
143    printf("SIMPRINTF(%d): ", (int)cvmx_get_core_num());
144    va_start(ap, format);
145    vprintf(format, ap);
146    va_end(ap);
147    cvmx_spinlock_unlock(&simprintf_lock);
148}
149
150
151/**
152 * Setup the CVMX_SHARED data section to be shared across
153 * all processors running this application. A memory mapped
154 * region is allocated using shm_open and mmap. The current
155 * contents of the CVMX_SHARED section are copied into the
156 * region. Then the new region is remapped to replace the
157 * existing CVMX_SHARED data.
158 *
159 * This function will display a message and abort the
160 * application under any error conditions. The Linux tmpfs
161 * filesystem must be mounted under /dev/shm.
162 */
163static void setup_cvmx_shared(void)
164{
165    const char *SHM_NAME = "cvmx_shared";
166    unsigned long shared_size = &__cvmx_shared_end - &__cvmx_shared_start;
167    int fd;
168
169    /* If there isn't and shared data we can skip all this */
170    if (shared_size)
171    {
172        char shm_name[30];
173        printf("CVMX_SHARED: %p-%p\n", &__cvmx_shared_start, &__cvmx_shared_end);
174
175#ifdef __UCLIBC__
176	const char *defaultdir = "/dev/shm/";
177	struct statfs f;
178	int pid;
179	/* The canonical place is /dev/shm. */
180	if (statfs (defaultdir, &f) == 0)
181	{
182	    pid = getpid();
183	    sprintf (shm_name, "%s%s-%d", defaultdir, SHM_NAME, pid);
184	}
185	else
186	{
187	    perror("/dev/shm is not mounted");
188	    exit(-1);
189	}
190
191	/* shm_open(), shm_unlink() are not implemented in uClibc. Do the
192	   same thing using open() and close() system calls.  */
193	fd = open (shm_name, O_RDWR | O_CREAT | O_TRUNC, 0);
194
195	if (fd < 0)
196	{
197	    perror("Failed to open CVMX_SHARED(shm_name)");
198	    exit(errno);
199	}
200
201	unlink (shm_name);
202#else
203	sprintf(shm_name, "%s-%d", SHM_NAME, getpid());
204        /* Open a new shared memory region for use as CVMX_SHARED */
205        fd = shm_open(shm_name, O_RDWR | O_CREAT | O_TRUNC, 0);
206        if (fd <0)
207        {
208            perror("Failed to setup CVMX_SHARED(shm_open)");
209            exit(errno);
210        }
211
212        /* We don't want the file on the filesystem. Immediately unlink it so
213            another application can create its own shared region */
214        shm_unlink(shm_name);
215#endif
216
217        /* Resize the region to match the size of CVMX_SHARED */
218        ftruncate(fd, shared_size);
219
220        /* Map the region into some random location temporarily so we can
221            copy the shared data to it */
222        void *ptr = mmap(NULL, shared_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
223        if (ptr == NULL)
224        {
225            perror("Failed to setup CVMX_SHARED(mmap copy)");
226            exit(errno);
227        }
228
229        /* Copy CVMX_SHARED to the new shared region so we don't lose
230            initializers */
231        memcpy(ptr, &__cvmx_shared_start, shared_size);
232        munmap(ptr, shared_size);
233
234        /* Remap the shared region to replace the old CVMX_SHARED region */
235        ptr = mmap(&__cvmx_shared_start, shared_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
236        if (ptr == NULL)
237        {
238            perror("Failed to setup CVMX_SHARED(mmap final)");
239            exit(errno);
240        }
241
242        /* Once mappings are setup, the file handle isn't needed anymore */
243        close(fd);
244    }
245}
246
247
248/**
249 * Shutdown and free the shared CVMX_SHARED region setup by
250 * setup_cvmx_shared.
251 */
252static void shutdown_cvmx_shared(void)
253{
254    unsigned long shared_size = &__cvmx_shared_end - &__cvmx_shared_start;
255    if (shared_size)
256        munmap(&__cvmx_shared_start, shared_size);
257}
258
259
260/**
261 * Setup access to the CONFIG_CAVIUM_RESERVE32 memory section
262 * created by the kernel. This memory is used for shared
263 * hardware buffers with 32 bit userspace applications.
264 */
265static void setup_reserve32(void)
266{
267    if (linux_mem32_min && linux_mem32_max)
268    {
269        int region_size = linux_mem32_max - linux_mem32_min + 1;
270        int mmap_flags = MAP_SHARED;
271        void *linux_mem32_base_ptr = NULL;
272
273        /* Although not strictly necessary, we are going to mmap() the wired
274            TLB region so it is in the process page tables. These pages will
275            never fault in, but they will allow GDB to access the wired
276            region. We need the mappings to exactly match the wired TLB
277            entry. */
278        if (linux_mem32_wired)
279        {
280            mmap_flags |= MAP_FIXED;
281            linux_mem32_base_ptr = CASTPTR(void, (1ull<<31) - region_size);
282        }
283
284        int fd = open("/dev/mem", O_RDWR);
285        if (fd < 0)
286        {
287            perror("ERROR opening /dev/mem");
288            exit(-1);
289        }
290
291        linux_mem32_base_ptr = mmap64(linux_mem32_base_ptr,
292                                          region_size,
293                                          PROT_READ | PROT_WRITE,
294                                          mmap_flags,
295                                          fd,
296                                          linux_mem32_min);
297        close(fd);
298
299        if (MAP_FAILED == linux_mem32_base_ptr)
300        {
301            perror("Error mapping reserve32");
302            exit(-1);
303        }
304
305        linux_mem32_offset = CAST64(linux_mem32_base_ptr) - linux_mem32_min;
306    }
307}
308
309
310/**
311 * Main entrypoint of the application. Here we setup shared
312 * memory and fork processes for each cpu. This simulates the
313 * normal simple executive environment of one process per
314 * cpu core.
315 *
316 * @param argc   Number of command line arguments
317 * @param argv   The command line arguments
318 * @return Return value for the process
319 */
320int main(int argc, const char *argv[])
321{
322    CVMX_SHARED static cvmx_spinlock_t mask_lock = CVMX_SPINLOCK_UNLOCKED_INITIALIZER;
323    CVMX_SHARED static int32_t pending_fork;
324    unsigned long cpumask;
325    unsigned long cpu;
326    int firstcpu = 0;
327    int firstcore = 0;
328
329    cvmx_linux_enable_xkphys_access(0);
330    cvmx_sysinfo_linux_userspace_initialize();
331
332    if (sizeof(void*) == 4)
333    {
334        if (linux_mem32_min)
335            setup_reserve32();
336        else
337        {
338            printf("\nFailed to access 32bit shared memory region. Most likely the Kernel\n"
339                   "has not been configured for 32bit shared memory access. Check the\n"
340                   "kernel configuration.\n"
341                   "Aborting...\n\n");
342            exit(-1);
343        }
344    }
345
346    setup_cvmx_shared();
347    cvmx_bootmem_init(cvmx_sysinfo_get()->phy_mem_desc_addr);
348
349    /* Check to make sure the Chip version matches the configured version */
350    octeon_model_version_check(cvmx_get_proc_id());
351
352    /* Initialize configuration to set bpid, pkind, pko_port for all the
353       available ports connected. */
354    __cvmx_helper_cfg_init();
355
356    /* Get the list of logical cpus we should run on */
357    if (sched_getaffinity(0, sizeof(cpumask), (cpu_set_t*)&cpumask))
358    {
359        perror("sched_getaffinity failed");
360        exit(errno);
361    }
362
363    cvmx_sysinfo_t *system_info = cvmx_sysinfo_get();
364
365    cvmx_atomic_set32(&pending_fork, 1);
366
367    /* Get the lowest logical cpu */
368    firstcore = ffsl(cpumask) - 1;
369    cpumask ^= (1ull<<(firstcore));
370    while (1)
371    {
372        if (cpumask == 0)
373        {
374            cpu = firstcore;
375            firstcpu = 1;
376            break;
377        }
378        cpu = ffsl(cpumask) - 1;
379        /* Turn off the bit for this CPU number. We've counted him */
380        cpumask ^= (1ull<<cpu);
381        /* Increment the number of CPUs running this app */
382        cvmx_atomic_add32(&pending_fork, 1);
383        /* Flush all IO streams before the fork. Otherwise any buffered
384           data in the C library will be duplicated. This results in
385           duplicate output from a single print */
386        fflush(NULL);
387        /* Fork a process for the new CPU */
388        int pid = fork();
389        if (pid == 0)
390        {
391            break;
392        }
393        else if (pid == -1)
394        {
395            perror("Fork failed");
396            exit(errno);
397        }
398     }
399
400
401    /* Set affinity to lock me to the correct CPU */
402    cpumask = (1<<cpu);
403    if (sched_setaffinity(0, sizeof(cpumask), (cpu_set_t*)&cpumask))
404    {
405        perror("sched_setaffinity failed");
406        exit(errno);
407    }
408
409    cvmx_spinlock_lock(&mask_lock);
410    system_info->core_mask |= 1<<cvmx_get_core_num();
411    cvmx_atomic_add32(&pending_fork, -1);
412    if (cvmx_atomic_get32(&pending_fork) == 0)
413    {
414        cvmx_dprintf("Active coremask = 0x%x\n", system_info->core_mask);
415    }
416    if (firstcpu)
417        system_info->init_core = cvmx_get_core_num();
418    cvmx_spinlock_unlock(&mask_lock);
419
420    /* Spinning waiting for forks to complete */
421    while (cvmx_atomic_get32(&pending_fork)) {}
422
423    cvmx_coremask_barrier_sync(system_info->core_mask);
424
425    cvmx_linux_enable_xkphys_access(1);
426
427    int result = appmain(argc, argv);
428
429    /* Wait for all forks to complete. This needs to be the core that started
430        all of the forks. It may not be the lowest numbered core! */
431    if (cvmx_get_core_num() == system_info->init_core)
432    {
433        int num_waits;
434        CVMX_POP(num_waits, system_info->core_mask);
435        num_waits--;
436        while (num_waits--)
437        {
438            if (wait(NULL) == -1)
439                perror("CVMX: Wait for forked child failed\n");
440        }
441    }
442
443    shutdown_cvmx_shared();
444
445    return result;
446}
447