bcm-diag.c (7773B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 */ 7 8 #include <gmodule.h> /* Must be included first */ 9 10 #include <sal/core/sync.h> 11 #include <sal/core/thread.h> 12 #include <sal/core/dpc.h> 13 #include <sal/appl/sal.h> 14 #include <soc/portmod/portmod_internal.h> 15 16 #include <linux-bde.h> 17 18 #include <appl/diag/sysconf.h> 19 #include <appl/diag/system.h> 20 21 #include <bcm-core.h> 22 23 /* All shell io is done using a user/kernel proxy service. */ 24 #include <linux-uk-proxy.h> 25 26 27 MODULE_AUTHOR("Broadcom Corporation"); 28 MODULE_DESCRIPTION("BCM Diag Shell"); 29 MODULE_LICENSE("Proprietary"); 30 31 /* Debug output */ 32 static int debug; 33 LKM_MOD_PARAM(debug, "i", int, 0); 34 MODULE_PARM_DESC(debug, 35 "Set debug level (default 0)"); 36 37 static int boot_flags = 0; 38 LKM_MOD_PARAM(boot_flags, "i", int, 0); 39 MODULE_PARM_DESC(boot_flags, "boot flags"); 40 41 /* Module Information */ 42 #define MODULE_MAJOR 124 43 #define MODULE_MINOR 0 44 #define MODULE_NAME "linux-bcm-diag" 45 #define PROXY_SERVICE "BCM DIAG SHELL" 46 47 /* Maximum string we can handle in printf */ 48 #define PROXY_STRING_MAX (LUK_MAX_DATA_SIZE * 6) 49 50 /* Message buffer */ 51 #define DBUF_DATA_SIZE 128 52 typedef struct _dbuf_t { 53 char data[DBUF_DATA_SIZE]; 54 } dbuf_t; 55 #define DBUF_DATA(dbuf) dbuf->data 56 57 /* Buffer console messages from interrupt context */ 58 #define DBUF_CNT_MAX 32 59 static dbuf_t dbuf[DBUF_CNT_MAX]; 60 static dbuf_t dbuf_flush[DBUF_CNT_MAX]; 61 static volatile int dbuf_cnt; 62 static spinlock_t dbuf_lock; 63 static struct semaphore dbuf_sem; 64 65 /* Thread control flags */ 66 static volatile int _bcm_shell_running; 67 static sal_thread_t _bcm_shell_thread; 68 static sal_thread_t orig_main_thread; 69 70 extern int 71 tty_vprintf(const char* fmt, va_list args) 72 __attribute__ ((format (printf, 1, 0))); 73 74 extern int 75 tty_printf(const char* fmt, ...) 76 __attribute__ ((format (printf, 1, 2))); 77 78 /* Linux kernel threads must check signals explicitely. */ 79 static void 80 check_exit_signals(void) 81 { 82 if(signal_pending(current)) { 83 sal_dpc_term(); 84 sal_thread_exit(0); 85 } 86 } 87 88 static void 89 _tty_flush(void *p1, void *p2, void *p3, void *p4, void *p5) 90 { 91 int cnt; 92 int dbuf_flush_cnt; 93 unsigned long flags; 94 dbuf_t *d; 95 char *p; 96 97 /* Prevent flushing from multiple threads simultaneously */ 98 if (down_interruptible(&dbuf_sem) != 0) { 99 /* Something's wrong */ 100 return; 101 } 102 103 /* Copy pending data to flush buffer */ 104 spin_lock_irqsave(&dbuf_lock, flags); 105 memcpy(dbuf_flush, dbuf, dbuf_cnt * sizeof(dbuf_t)); 106 dbuf_flush_cnt = dbuf_cnt; 107 dbuf_cnt = 0; 108 spin_unlock_irqrestore(&dbuf_lock, flags); 109 110 /* Note that we may sleep during flush */ 111 for (cnt = 0; cnt < dbuf_flush_cnt; cnt++) { 112 d = &dbuf_flush[cnt]; 113 p = DBUF_DATA(d); 114 linux_uk_proxy_send(PROXY_SERVICE, p, strlen(p)); 115 } 116 117 up(&dbuf_sem); 118 } 119 120 int 121 tty_vprintf(const char* fmt, va_list args) 122 { 123 int cnt, tmp_cnt, offset=0; 124 unsigned long flags; 125 static char s[PROXY_STRING_MAX]; 126 127 if (sal_no_sleep()) { 128 /* Schedule flush function */ 129 if (dbuf_cnt == 0) { 130 sal_dpc(_tty_flush, 0, 0, 0, 0, 0); 131 } 132 /* Buffer message */ 133 spin_lock_irqsave(&dbuf_lock, flags); 134 if (dbuf_cnt < DBUF_CNT_MAX) { 135 dbuf_t *d = &dbuf[dbuf_cnt++]; 136 vsnprintf(DBUF_DATA(d), DBUF_DATA_SIZE-1, fmt, args); 137 } 138 spin_unlock_irqrestore(&dbuf_lock, flags); 139 return 0; 140 } 141 142 if (dbuf_cnt) { 143 /* Flush buffered messages */ 144 _tty_flush(0, 0, 0, 0, 0); 145 } 146 147 tmp_cnt = cnt = vsnprintf(s, PROXY_STRING_MAX - 1, fmt, args); 148 if (tmp_cnt >= PROXY_STRING_MAX) { 149 tmp_cnt = PROXY_STRING_MAX; 150 } 151 while (tmp_cnt > 0) { 152 linux_uk_proxy_send(PROXY_SERVICE, &s[offset], 153 (tmp_cnt < LUK_MAX_DATA_SIZE) ? tmp_cnt : LUK_MAX_DATA_SIZE); 154 tmp_cnt -= LUK_MAX_DATA_SIZE; 155 offset += LUK_MAX_DATA_SIZE; 156 } 157 check_exit_signals(); 158 return cnt; 159 } 160 161 /* 162 * Function: tty_printf 163 * 164 * Purpose: 165 * Application-specific console output function. 166 * Parameters: 167 * Same as standard C printf. 168 * Returns: 169 * Same as standard C printf. 170 */ 171 int 172 tty_printf(const char* fmt, ...) 173 { 174 va_list args; 175 va_start(args, fmt); 176 return tty_vprintf(fmt, args); 177 } 178 179 /* 180 * Function: tty_gets 181 * 182 * Purpose: 183 * Application-specific console input function. 184 * Parameters: 185 * Same as standard C fgets. 186 * Returns: 187 * Same as standard C fgets. 188 */ 189 char * 190 tty_gets(char* dst, int size) 191 { 192 linux_uk_proxy_recv(PROXY_SERVICE, dst, (unsigned int *)&size); 193 check_exit_signals(); 194 return dst; 195 } 196 197 /* 198 * Function: _bcm_shell 199 * 200 * Purpose: 201 * Thread entry used to run an instance of the BCM diag shell 202 * Parameters: 203 * None 204 * Returns: 205 * Nothing 206 */ 207 static void 208 _bcm_shell(void* p) 209 { 210 if (sal_core_init() < 0 || sal_appl_init() < 0) { 211 gprintk("SAL Initialization failed\n"); 212 sal_thread_exit(0); 213 } 214 _bcm_shell_thread = sal_thread_self(); 215 216 if (boot_flags) { 217 sal_boot_flags_set(boot_flags); 218 } 219 220 if (debug >= 1) gprintk("BCM Diag Module Initialized. Starting proxy...\n"); 221 222 /* A small delay here prevents the telnet proxy 223 * from choking on the first command. 224 */ 225 sal_usleep(100*1000); 226 227 _bcm_shell_running = 1; 228 if (debug >= 1) gprintk("Starting Diag Shell...\n"); 229 diag_shell(); 230 if (debug >= 1) gprintk("Diag Shell is done.\n"); 231 sal_dpc_term(); 232 linux_bde_destroy(bde); 233 _bcm_shell_running = 0; 234 } 235 236 /* 237 * Generic module functions 238 */ 239 240 /* 241 * Function: _pprint 242 * 243 * Purpose: 244 * Print proc filesystem information. 245 * Parameters: 246 * None 247 * Returns: 248 * Always 0 249 */ 250 static int 251 _pprint(void) 252 { 253 pprintf("Broadcom Linux BCM Diagnostic Shell\n"); 254 pprintf("\tProxy Service: '%s'\n", PROXY_SERVICE); 255 return 0; 256 } 257 258 /* 259 * Function: _init 260 * 261 * Purpose: 262 * Module initialization. 263 * Starts the BCM diag thread. 264 * Parameters: 265 * None 266 * Returns: 267 * Always 0 268 */ 269 static int 270 _init(void) 271 { 272 spin_lock_init(&dbuf_lock); 273 sema_init(&dbuf_sem, 1); 274 orig_main_thread = sal_thread_main_get(); 275 linux_uk_proxy_service_create(PROXY_SERVICE, 1, 0); 276 sal_thread_create("bcm-shell", 0, 0, _bcm_shell, 0); 277 return 0; 278 } 279 280 /* 281 * Function: _cleanup 282 * 283 * Purpose: 284 * Module cleanup function 285 * Parameters: 286 * None 287 * Returns: 288 * Always 0 289 * Notes: 290 * The BCM diag thread will be destroyed to avoid page faults. 291 */ 292 static int 293 _cleanup(void) 294 { 295 /* Restore debug print vectors */ 296 bcore_debug_set_default(); 297 298 /* Restore assert handler */ 299 bcore_assert_set_default(); 300 301 /* Close the shell */ 302 if (_bcm_shell_thread) { 303 sal_thread_destroy(_bcm_shell_thread); 304 sal_usleep(200000); 305 } 306 /* This should only be relavant on a really busy system */ 307 if (_bcm_shell_running) { 308 sal_usleep(200000); 309 } 310 311 linux_uk_proxy_service_destroy(PROXY_SERVICE); 312 313 /* Restore main thread */ 314 sal_thread_main_set(orig_main_thread); 315 316 return 0; 317 } 318 319 /* Module vectors */ 320 static gmodule_t _gmodule = { 321 name: MODULE_NAME, 322 major: MODULE_MAJOR, 323 minor: MODULE_MINOR, 324 init: _init, 325 cleanup: _cleanup, 326 pprint: _pprint, 327 ioctl: NULL, 328 open: NULL, 329 close: NULL, 330 }; 331 332 gmodule_t* 333 gmodule_get(void) 334 { 335 #ifdef LKM_2_4 336 EXPORT_NO_SYMBOLS; 337 #endif 338 return &_gmodule; 339 } 340 341 #ifdef LKM_2_6 342 EXPORT_SYMBOL(tty_vprintf); 343 EXPORT_SYMBOL(tty_printf); 344 EXPORT_SYMBOL(tty_gets); 345 #endif 346 347 #if defined(INCLUDE_REGEX) 348 #include <appl/diag/cmdlist.h> 349 EXPORT_SYMBOL(cmd_regex); 350 EXPORT_SYMBOL(cmd_regex_usage); 351 #endif /* INCLUDE_REGEX */ 352 353 EXPORT_SYMBOL(portmod_pm_info_get);