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