1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28
29// ---- Include Files -------------------------------------------------------
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <getopt.h>
35#include <errno.h>
36#include <signal.h>
37#include <assert.h>
38
39#include <user-vcsm.h>
40#include "interface/vcos/vcos.h"
41
42// ---- Public Variables ----------------------------------------------------
43
44// ---- Private Constants and Types -----------------------------------------
45
46// Note: Exact match 32 chars.
47static char blah_blah[32] = "Testing shared memory interface!";
48
49enum
50{
51 OPT_ALLOC = 'a',
52 OPT_STATUS = 's',
53 OPT_PID = 'p',
54 OPT_HELP = 'h',
55
56 // Options from this point onwards don't have any short option equivalents
57 OPT_FIRST_LONG_OPT = 0x80,
58};
59
60static struct option long_opts[] =
61{
62 // name has_arg flag val
63 // ------------------- ------------------ ---- ---------------
64 { "alloc", required_argument, NULL, OPT_ALLOC },
65 { "pid", required_argument, NULL, OPT_PID },
66 { "status", required_argument, NULL, OPT_STATUS },
67 { "help", no_argument, NULL, OPT_HELP },
68 { 0, 0, 0, 0 }
69};
70
71// Maximum length of option string (3 characters max for each option + NULL)
72#define OPTSTRING_LEN ( sizeof( long_opts ) / sizeof( *long_opts ) * 3 + 1 )
73
74// ---- Private Variables ---------------------------------------------------
75
76static VCOS_LOG_CAT_T smem_log_category;
77#define VCOS_LOG_CATEGORY (&smem_log_category)
78static VCOS_EVENT_T quit_event;
79
80// ---- Private Functions ---------------------------------------------------
81
82static void show_usage( void )
83{
84 vcos_log_info( "Usage: smem [OPTION]..." );
85 vcos_log_info( " -a, --alloc=SIZE Allocates a block of SIZE" );
86 vcos_log_info( " -p, --pid=PID Use PID for desired action" );
87 vcos_log_info( " -s, --status=TYPE Queries status of TYPE [for PID]" );
88 vcos_log_info( " all all (for current pid)" );
89 vcos_log_info( " vc videocore allocations" );
90 vcos_log_info( " map host map status" );
91 vcos_log_info( " map <pid> host map status for pid" );
92 vcos_log_info( " host <pid> host allocations for pid" );
93 vcos_log_info( " -h, --help Print this information" );
94}
95
96static void create_optstring( char *optstring )
97{
98 char *short_opts = optstring;
99 struct option *option;
100
101 // Figure out the short options from our options structure
102 for ( option = long_opts; option->name != NULL; option++ )
103 {
104 if (( option->flag == NULL ) && ( option->val < OPT_FIRST_LONG_OPT ))
105 {
106 *short_opts++ = (char)option->val;
107
108 if ( option->has_arg != no_argument )
109 {
110 *short_opts++ = ':';
111 }
112
113 // Optional arguments require two ':'
114 if ( option->has_arg == optional_argument )
115 {
116 *short_opts++ = ':';
117 }
118 }
119 }
120 *short_opts++ = '\0';
121}
122
123static int get_status( VCSM_STATUS_T mode, int pid )
124{
125 switch ( mode )
126 {
127 case VCSM_STATUS_VC_WALK_ALLOC:
128 vcsm_status( VCSM_STATUS_VC_WALK_ALLOC, -1 );
129 break;
130
131 case VCSM_STATUS_HOST_WALK_MAP:
132 if ( pid != -1 )
133 {
134 vcsm_status( VCSM_STATUS_HOST_WALK_PID_MAP, pid );
135 }
136 else
137 {
138 vcsm_status( VCSM_STATUS_HOST_WALK_MAP, -1 );
139 }
140 break;
141
142 case VCSM_STATUS_HOST_WALK_PID_ALLOC:
143 vcsm_status( VCSM_STATUS_HOST_WALK_PID_ALLOC, pid );
144 break;
145
146 case VCSM_STATUS_VC_MAP_ALL:
147 vcsm_status( VCSM_STATUS_VC_WALK_ALLOC, -1 );
148 vcsm_status( VCSM_STATUS_HOST_WALK_MAP, -1 );
149 break;
150
151 default:
152 break;
153 }
154
155 return 0;
156}
157
158static void control_c( int signum )
159{
160 (void)signum;
161
162 vcos_log_info( "Shutting down..." );
163
164 vcos_event_signal( &quit_event );
165}
166
167static int start_monitor( void )
168{
169 if ( vcos_event_create( &quit_event, "smem" ) != VCOS_SUCCESS )
170 {
171 vcos_log_info( "Failed to create quit event" );
172
173 return -1;
174 }
175
176 // Handle the INT and TERM signals so we can quit
177 signal( SIGINT, control_c );
178 signal( SIGTERM, control_c );
179
180 return 0;
181}
182
183// ---- Public Functions -----------------------------------------------------
184
185// #define DOUBLE_ALLOC
186#define RESIZE_ALLOC
187
188int main( int argc, char **argv )
189{
190 int32_t ret;
191 char optstring[OPTSTRING_LEN];
192 int opt;
193 int opt_alloc = 0;
194 int opt_status = 0;
195 uint32_t alloc_size = 0;
196 int opt_pid = -1;
197 VCSM_STATUS_T status_mode = VCSM_STATUS_NONE;
198
199 void *usr_ptr_1;
200 unsigned int usr_hdl_1;
201#if defined(DOUBLE_ALLOC) || defined(RESIZE_ALLOC)
202 void *usr_ptr_2;
203 unsigned int usr_hdl_2;
204#endif
205
206 // Initialize VCOS
207 vcos_init();
208
209 vcos_log_set_level(&smem_log_category, VCOS_LOG_INFO);
210 smem_log_category.flags.want_prefix = 0;
211 vcos_log_register( "smem", &smem_log_category );
212
213 // Create the option string that we will be using to parse the arguments
214 create_optstring( optstring );
215
216 // Parse the command line arguments
217 while (( opt = getopt_long_only( argc, argv, optstring, long_opts,
218 NULL )) != -1 )
219 {
220 switch ( opt )
221 {
222 case 0:
223 {
224 // getopt_long returns 0 for entries where flag is non-NULL
225 break;
226 }
227 case OPT_ALLOC:
228 {
229 char *end;
230 alloc_size = (uint32_t)strtoul( optarg, &end, 10 );
231 if (end == optarg)
232 {
233 vcos_log_info( "Invalid arguments '%s'", optarg );
234 goto err_out;
235 }
236
237 opt_alloc = 1;
238 break;
239 }
240 case OPT_PID:
241 {
242 char *end;
243 opt_pid = (int)strtol( optarg, &end, 10 );
244 if (end == optarg)
245 {
246 vcos_log_info( "Invalid arguments '%s'", optarg );
247 goto err_out;
248 }
249
250 break;
251 }
252 case OPT_STATUS:
253 {
254 char status_str[32];
255
256 /* coverity[secure_coding] String length specified, so can't overflow */
257 if ( sscanf( optarg, "%31s", status_str ) != 1 )
258 {
259 vcos_log_info( "Invalid arguments '%s'", optarg );
260 goto err_out;
261 }
262
263 if ( vcos_strcasecmp( status_str, "all" ) == 0 )
264 {
265 status_mode = VCSM_STATUS_VC_MAP_ALL;
266 }
267 else if ( vcos_strcasecmp( status_str, "vc" ) == 0 )
268 {
269 status_mode = VCSM_STATUS_VC_WALK_ALLOC;
270 }
271 else if ( vcos_strcasecmp( status_str, "map" ) == 0 )
272 {
273 status_mode = VCSM_STATUS_HOST_WALK_MAP;
274 }
275 else if ( vcos_strcasecmp( status_str, "host" ) == 0 )
276 {
277 status_mode = VCSM_STATUS_HOST_WALK_PID_ALLOC;
278 }
279 else
280 {
281 goto err_out;
282 }
283
284 opt_status = 1;
285 break;
286 }
287 default:
288 {
289 vcos_log_info( "Unrecognized option '%d'", opt );
290 goto err_usage;
291 }
292 case '?':
293 case OPT_HELP:
294 {
295 goto err_usage;
296 }
297 } // end switch
298 } // end while
299
300 argc -= optind;
301 argv += optind;
302
303 if (( optind == 1 ) || ( argc > 0 ))
304 {
305 if ( argc > 0 )
306 {
307 vcos_log_info( "Unrecognized argument -- '%s'", *argv );
308 }
309
310 goto err_usage;
311 }
312
313 // Start the shared memory support.
314 if ( vcsm_init() == -1 )
315 {
316 vcos_log_info( "Cannot initialize smem device" );
317 goto err_out;
318 }
319
320 if ( opt_alloc == 1 )
321 {
322 vcos_log_info( "Allocating 2 times %u-bytes in shared memory", alloc_size );
323
324 usr_hdl_1 = vcsm_malloc( alloc_size,
325 "smem-test-alloc" );
326
327 vcos_log_info( "Allocation 1 result: user %x, vc-hdl %x",
328 usr_hdl_1, vcsm_vc_hdl_from_hdl( usr_hdl_1 ) );
329
330#if defined(DOUBLE_ALLOC) || defined(RESIZE_ALLOC)
331 usr_hdl_2 = vcsm_malloc( alloc_size,
332 NULL );
333 vcos_log_info( "Allocation 2 result: user %x",
334 usr_hdl_2 );
335
336 usr_ptr_2 = vcsm_lock( usr_hdl_2 );
337 vcos_log_info( "Allocation 2 : lock %p",
338 usr_ptr_2 );
339 vcos_log_info( "Allocation 2 : unlock %d",
340 vcsm_unlock_hdl( usr_hdl_2 ) );
341#endif
342
343 // Do a simple write/read test.
344 if ( usr_hdl_1 != 0 )
345 {
346 usr_ptr_1 = vcsm_lock( usr_hdl_1 );
347 vcos_log_info( "Allocation 1 : lock %p",
348 usr_ptr_1 );
349 if ( usr_ptr_1 )
350 {
351 memset ( usr_ptr_1,
352 0,
353 alloc_size );
354 memcpy ( usr_ptr_1,
355 blah_blah,
356 32 );
357 vcos_log_info( "Allocation 1 contains: \"%s\"",
358 (char *)usr_ptr_1 );
359
360 vcos_log_info( "Allocation 1: vc-hdl %x",
361 vcsm_vc_hdl_from_ptr ( usr_ptr_1 ) );
362 vcos_log_info( "Allocation 1: usr-hdl %x",
363 vcsm_usr_handle ( usr_ptr_1 ) );
364 vcos_log_info( "Allocation 1 : unlock %d",
365 vcsm_unlock_ptr( usr_ptr_1 ) );
366 }
367
368 usr_ptr_1 = vcsm_lock( usr_hdl_1 );
369 vcos_log_info( "Allocation 1 (relock) : lock %p",
370 usr_ptr_1 );
371 if ( usr_ptr_1 )
372 {
373 vcos_log_info( "Allocation 1 (relock) : unlock %d",
374 vcsm_unlock_hdl( usr_hdl_1 ) );
375 }
376 }
377
378#if defined(RESIZE_ALLOC)
379 ret = vcsm_resize( usr_hdl_1, 2 * alloc_size );
380 vcos_log_info( "Allocation 1 : resize %d", ret );
381 if ( ret == 0 )
382 {
383 usr_ptr_1 = vcsm_lock( usr_hdl_1 );
384 vcos_log_info( "Allocation 1 (resize) : lock %p",
385 usr_ptr_1 );
386 if ( usr_ptr_1 )
387 {
388 memset ( usr_ptr_1,
389 0,
390 2 * alloc_size );
391 memcpy ( usr_ptr_1,
392 blah_blah,
393 32 );
394 vcos_log_info( "Allocation 1 (resized) contains: \"%s\"",
395 (char *)usr_ptr_1 );
396 vcos_log_info( "Allocation 1 (resized) : unlock %d",
397 vcsm_unlock_ptr( usr_ptr_1 ) );
398 }
399 }
400
401 // This checks that the memory can be remapped properly
402 // because the Block 1 expanded beyond Block 2 boundary.
403 //
404 usr_ptr_2 = vcsm_lock( usr_hdl_2 );
405 vcos_log_info( "Allocation 2 : lock %p",
406 usr_ptr_2 );
407 vcos_log_info( "Allocation 2 : unlock %d",
408 vcsm_unlock_hdl( usr_hdl_2 ) );
409
410 // This checks that we can free a memory block even if it
411 // is locked, which could be the case if the application
412 // dies.
413 //
414 usr_ptr_2 = vcsm_lock( usr_hdl_2 );
415 vcos_log_info( "Allocation 2 : lock %p",
416 usr_ptr_2 );
417 vcsm_free ( usr_hdl_2 );
418#endif
419
420#if defined(DOUBLE_ALLOC)
421#endif
422 }
423
424 if ( opt_status == 1 )
425 {
426 get_status( status_mode, opt_pid );
427 }
428
429 // If we allocated something, wait for the signal to exit to give chance for the
430 // user to poke around the allocation test.
431 //
432 if ( opt_alloc == 1 )
433 {
434 start_monitor();
435
436 vcos_event_wait( &quit_event );
437 vcos_event_delete( &quit_event );
438 }
439
440 // Terminate the shared memory support.
441 vcsm_exit ();
442 goto err_out;
443
444err_usage:
445 show_usage();
446
447err_out:
448 exit( 1 );
449}
450