]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/contrib/octeon-sdk/cvmx-app-init-linux.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / contrib / octeon-sdk / cvmx-app-init-linux.c
1 /***********************license start***************
2  * Copyright (c) 2003-2010  Cavium Networks (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 Networks 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  NETWORKS 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: 49448 $<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
92 int octeon_model_version_check(uint32_t chip_id);
93
94 #define OCTEON_ECLOCK_MULT_INPUT_X16    ((int)(33.4*16))
95
96 /* Applications using the simple executive libraries under Linux userspace must
97     rename their "main" function to match the prototype below. This allows the
98     simple executive to perform needed memory initialization and process
99     creation before the application runs. */
100 extern int appmain(int argc, const char *argv[]);
101
102 /* These two external addresses provide the beginning and end markers for the
103     CVMX_SHARED section. These are defined by the cvmx-shared.ld linker script.
104     If they aren't defined, you probably forgot to link using this script. */
105 extern void __cvmx_shared_start;
106 extern void __cvmx_shared_end;
107 extern uint64_t linux_mem32_min;
108 extern uint64_t linux_mem32_max;
109 extern uint64_t linux_mem32_wired;
110 extern uint64_t linux_mem32_offset;
111
112 /**
113  * This function performs some default initialization of the Octeon executive.  It initializes
114  * the cvmx_bootmem memory allocator with the list of physical memory shared by the bootloader.
115  * This function should be called on all cores that will use the bootmem allocator.
116  * Applications which require a different configuration can replace this function with a suitable application
117  * specific one.
118  *
119  * @return 0 on success
120  *         -1 on failure
121  */
122 int cvmx_user_app_init(void)
123 {
124     return 0;
125 }
126
127
128 /**
129  * Simulator magic is not supported in user mode under Linux.
130  * This version of simprintf simply calls the underlying C
131  * library printf for output. It also makes sure that two
132  * calls to simprintf provide atomic output.
133  *
134  * @param format  Format string in the same format as printf.
135  */
136 void simprintf(const char *format, ...)
137 {
138     CVMX_SHARED static cvmx_spinlock_t simprintf_lock = CVMX_SPINLOCK_UNLOCKED_INITIALIZER;
139     va_list ap;
140
141     cvmx_spinlock_lock(&simprintf_lock);
142     printf("SIMPRINTF(%d): ", (int)cvmx_get_core_num());
143     va_start(ap, format);
144     vprintf(format, ap);
145     va_end(ap);
146     cvmx_spinlock_unlock(&simprintf_lock);
147 }
148
149
150 /**
151  * Setup the CVMX_SHARED data section to be shared across
152  * all processors running this application. A memory mapped
153  * region is allocated using shm_open and mmap. The current
154  * contents of the CVMX_SHARED section are copied into the
155  * region. Then the new region is remapped to replace the
156  * existing CVMX_SHARED data.
157  *
158  * This function will display a message and abort the
159  * application under any error conditions. The Linux tmpfs
160  * filesystem must be mounted under /dev/shm.
161  */
162 static void setup_cvmx_shared(void)
163 {
164     const char *SHM_NAME = "cvmx_shared";
165     unsigned long shared_size = &__cvmx_shared_end - &__cvmx_shared_start;
166     int fd;
167
168     /* If there isn't and shared data we can skip all this */
169     if (shared_size)
170     {
171         char shm_name[30];
172         printf("CVMX_SHARED: %p-%p\n", &__cvmx_shared_start, &__cvmx_shared_end);
173
174 #ifdef __UCLIBC__
175         const char *defaultdir = "/dev/shm/";
176         struct statfs f;
177         int pid;
178         /* The canonical place is /dev/shm. */
179         if (statfs (defaultdir, &f) == 0)
180         {
181             pid = getpid();
182             sprintf (shm_name, "%s%s-%d", defaultdir, SHM_NAME, pid);
183         }
184         else
185         {
186             perror("/dev/shm is not mounted");
187             exit(-1);
188         }
189
190         /* shm_open(), shm_unlink() are not implemented in uClibc. Do the
191            same thing using open() and close() system calls.  */
192         fd = open (shm_name, O_RDWR | O_CREAT | O_TRUNC, 0);
193
194         if (fd < 0)
195         {
196             perror("Failed to open CVMX_SHARED(shm_name)");
197             exit(errno);
198         }
199
200         unlink (shm_name);
201 #else
202         sprintf(shm_name, "%s-%d", SHM_NAME, getpid());
203         /* Open a new shared memory region for use as CVMX_SHARED */
204         fd = shm_open(shm_name, O_RDWR | O_CREAT | O_TRUNC, 0);
205         if (fd <0)
206         {
207             perror("Failed to setup CVMX_SHARED(shm_open)");
208             exit(errno);
209         }
210
211         /* We don't want the file on the filesystem. Immediately unlink it so
212             another application can create its own shared region */
213         shm_unlink(shm_name);
214 #endif
215
216         /* Resize the region to match the size of CVMX_SHARED */
217         ftruncate(fd, shared_size);
218
219         /* Map the region into some random location temporarily so we can
220             copy the shared data to it */
221         void *ptr = mmap(NULL, shared_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
222         if (ptr == NULL)
223         {
224             perror("Failed to setup CVMX_SHARED(mmap copy)");
225             exit(errno);
226         }
227
228         /* Copy CVMX_SHARED to the new shared region so we don't lose
229             initializers */
230         memcpy(ptr, &__cvmx_shared_start, shared_size);
231         munmap(ptr, shared_size);
232
233         /* Remap the shared region to replace the old CVMX_SHARED region */
234         ptr = mmap(&__cvmx_shared_start, shared_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
235         if (ptr == NULL)
236         {
237             perror("Failed to setup CVMX_SHARED(mmap final)");
238             exit(errno);
239         }
240
241         /* Once mappings are setup, the file handle isn't needed anymore */
242         close(fd);
243     }
244 }
245
246
247 /**
248  * Shutdown and free the shared CVMX_SHARED region setup by
249  * setup_cvmx_shared.
250  */
251 static void shutdown_cvmx_shared(void)
252 {
253     unsigned long shared_size = &__cvmx_shared_end - &__cvmx_shared_start;
254     if (shared_size)
255         munmap(&__cvmx_shared_start, shared_size);
256 }
257
258
259 /**
260  * Setup access to the CONFIG_CAVIUM_RESERVE32 memory section
261  * created by the kernel. This memory is used for shared
262  * hardware buffers with 32 bit userspace applications.
263  */
264 static void setup_reserve32(void)
265 {
266     if (linux_mem32_min && linux_mem32_max)
267     {
268         int region_size = linux_mem32_max - linux_mem32_min + 1;
269         int mmap_flags = MAP_SHARED;
270         void *linux_mem32_base_ptr = NULL;
271
272         /* Although not strictly necessary, we are going to mmap() the wired
273             TLB region so it is in the process page tables. These pages will
274             never fault in, but they will allow GDB to access the wired
275             region. We need the mappings to exactly match the wired TLB
276             entry. */
277         if (linux_mem32_wired)
278         {
279             mmap_flags |= MAP_FIXED;
280             linux_mem32_base_ptr = CASTPTR(void, (1ull<<31) - region_size);
281         }
282
283         int fd = open("/dev/mem", O_RDWR);
284         if (fd < 0)
285         {
286             perror("ERROR opening /dev/mem");
287             exit(-1);
288         }
289
290         linux_mem32_base_ptr = mmap64(linux_mem32_base_ptr,
291                                           region_size,
292                                           PROT_READ | PROT_WRITE,
293                                           mmap_flags,
294                                           fd,
295                                           linux_mem32_min);
296         close(fd);
297
298         if (MAP_FAILED == linux_mem32_base_ptr)
299         {
300             perror("Error mapping reserve32");
301             exit(-1);
302         }
303
304         linux_mem32_offset = CAST64(linux_mem32_base_ptr) - linux_mem32_min;
305     }
306 }
307
308
309 /**
310  * Main entrypoint of the application. Here we setup shared
311  * memory and fork processes for each cpu. This simulates the
312  * normal simple executive environment of one process per
313  * cpu core.
314  *
315  * @param argc   Number of command line arguments
316  * @param argv   The command line arguments
317  * @return Return value for the process
318  */
319 int main(int argc, const char *argv[])
320 {
321     CVMX_SHARED static cvmx_spinlock_t mask_lock = CVMX_SPINLOCK_UNLOCKED_INITIALIZER;
322     CVMX_SHARED static int32_t pending_fork;
323     unsigned long cpumask;
324     unsigned long cpu;
325     int firstcpu = 0;
326     int firstcore = 0;
327
328     cvmx_linux_enable_xkphys_access(0);
329
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     /* Get the list of logical cpus we should run on */
353     if (sched_getaffinity(0, sizeof(cpumask), (cpu_set_t*)&cpumask))
354     {
355         perror("sched_getaffinity failed");
356         exit(errno);
357     }
358
359     cvmx_sysinfo_t *system_info = cvmx_sysinfo_get();
360
361     cvmx_atomic_set32(&pending_fork, 1);
362
363     /* Get the lowest logical cpu */
364     firstcore = ffsl(cpumask) - 1;
365     cpumask ^= (1<<(firstcore));
366     while (1)
367     {
368         if (cpumask == 0)
369         {
370             cpu = firstcore;
371             firstcpu = 1;
372             break;
373         }
374         cpu = ffsl(cpumask) - 1;
375         /* Turn off the bit for this CPU number. We've counted him */
376         cpumask ^= (1<<cpu);
377         /* Increment the number of CPUs running this app */
378          cvmx_atomic_add32(&pending_fork, 1);
379         /* Flush all IO streams before the fork. Otherwise any buffered
380            data in the C library will be duplicated. This results in
381            duplicate output from a single print */
382         fflush(NULL);
383         /* Fork a process for the new CPU */
384         int pid = fork();
385         if (pid == 0)
386         {
387             break;
388         }
389         else if (pid == -1)
390         {
391             perror("Fork failed");
392             exit(errno);
393         }
394      }
395
396
397     /* Set affinity to lock me to the correct CPU */
398     cpumask = (1<<cpu);
399     if (sched_setaffinity(0, sizeof(cpumask), (cpu_set_t*)&cpumask))
400     {
401         perror("sched_setaffinity failed");
402         exit(errno);
403     }
404
405     cvmx_spinlock_lock(&mask_lock);
406     system_info->core_mask |= 1<<cvmx_get_core_num();
407     cvmx_atomic_add32(&pending_fork, -1);
408     if (cvmx_atomic_get32(&pending_fork) == 0)
409         cvmx_dprintf("Active coremask = 0x%x\n", system_info->core_mask);
410     if (firstcpu)
411         system_info->init_core = cvmx_get_core_num();
412     cvmx_spinlock_unlock(&mask_lock);
413
414     /* Spinning waiting for forks to complete */
415     while (cvmx_atomic_get32(&pending_fork)) {}
416
417     cvmx_coremask_barrier_sync(system_info->core_mask);
418
419     cvmx_linux_enable_xkphys_access(1);
420
421     int result = appmain(argc, argv);
422
423     /* Wait for all forks to complete. This needs to be the core that started
424         all of the forks. It may not be the lowest numbered core! */
425     if (cvmx_get_core_num() == system_info->init_core)
426     {
427         int num_waits;
428         CVMX_POP(num_waits, system_info->core_mask);
429         num_waits--;
430         while (num_waits--)
431         {
432             if (wait(NULL) == -1)
433                 perror("CVMX: Wait for forked child failed\n");
434         }
435     }
436
437     shutdown_cvmx_shared();
438
439     return result;
440 }