| 1 | /* |
| 2 | Copyright (c) 2012, Broadcom Europe Ltd |
| 3 | All rights reserved. |
| 4 | |
| 5 | Redistribution and use in source and binary forms, with or without |
| 6 | modification, 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 | |
| 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
| 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 23 | ON 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 |
| 25 | SOFTWARE, 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. |
| 47 | static char blah_blah[32] = "Testing shared memory interface!" ; |
| 48 | |
| 49 | enum |
| 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 | |
| 60 | static 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 | |
| 76 | static VCOS_LOG_CAT_T smem_log_category; |
| 77 | #define VCOS_LOG_CATEGORY (&smem_log_category) |
| 78 | static VCOS_EVENT_T quit_event; |
| 79 | |
| 80 | // ---- Private Functions --------------------------------------------------- |
| 81 | |
| 82 | static 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 | |
| 96 | static 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 | |
| 123 | static 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 | |
| 158 | static 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 | |
| 167 | static 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 | |
| 188 | int 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 | |
| 444 | err_usage: |
| 445 | show_usage(); |
| 446 | |
| 447 | err_out: |
| 448 | exit( 1 ); |
| 449 | } |
| 450 | |