uspace_common.h
1 // Copyright 2006-2014, various authors 2 // 3 // This program is free software; you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation; either version 2 of the License, or 6 // (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with this program; if not, write to the Free Software 15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 16 #include <sys/time.h> 17 #include <time.h> 18 #include <stdio.h> 19 #include <sys/utsname.h> 20 #include <string.h> 21 #include <unistd.h> 22 #include <sys/types.h> 23 #include <sys/stat.h> 24 25 #include <rtapi_errno.h> 26 #include <rtapi_mutex.h> 27 static int msg_level = RTAPI_MSG_ERR; /* message printing level */ 28 29 #include <sys/ipc.h> /* IPC_* */ 30 #include <sys/shm.h> /* shmget() */ 31 /* These structs hold data associated with objects like tasks, etc. */ 32 /* Task handles are pointers to these structs. */ 33 34 #include "config.h" 35 36 #ifdef RTAPI 37 #include "rtapi_uspace.hh" 38 #endif 39 40 typedef struct { 41 int magic; /* to check for valid handle */ 42 int key; /* key to shared memory area */ 43 int id; /* OS identifier for shmem */ 44 int count; /* count of maps in this process */ 45 unsigned long int size; /* size of shared memory area */ 46 void *mem; /* pointer to the memory */ 47 } rtapi_shmem_handle; 48 49 #define MAX_SHM 64 50 51 #define SHMEM_MAGIC 25453 /* random numbers used as signatures */ 52 53 static rtapi_shmem_handle shmem_array[MAX_SHM] = {{0},}; 54 55 int rtapi_shmem_new(int key, int module_id, unsigned long int size) 56 { 57 #ifdef RTAPI 58 WITH_ROOT; 59 #endif 60 rtapi_shmem_handle *shmem; 61 int i; 62 63 for(i=0 ; i < MAX_SHM; i++) { 64 if(shmem_array[i].magic == SHMEM_MAGIC && shmem_array[i].key == key) { 65 shmem_array[i].count ++; 66 return i; 67 } 68 if(shmem_array[i].magic != SHMEM_MAGIC) break; 69 } 70 if(i == MAX_SHM) 71 { 72 rtapi_print_msg(RTAPI_MSG_ERR, "rtapi_shmem_new failed due to MAX_SHM\n"); 73 return -ENOMEM; 74 } 75 shmem = &shmem_array[i]; 76 77 /* now get shared memory block from OS */ 78 shmem->id = shmget((key_t) key, (int) size, IPC_CREAT | 0600); 79 if (shmem->id == -1) { 80 rtapi_print_msg(RTAPI_MSG_ERR, "rtapi_shmem_new failed due to shmget(key=0x%08x): %s\n", key, strerror(errno)); 81 return -errno; 82 } 83 84 struct shmid_ds stat; 85 int res = shmctl(shmem->id, IPC_STAT, &stat); 86 if(res < 0) perror("shmctl IPC_STAT"); 87 88 #ifdef RTAPI 89 /* ensure the segment is owned by user, not root */ 90 if(geteuid() == 0) { 91 stat.shm_perm.uid = ruid; 92 res = shmctl(shmem->id, IPC_SET, &stat); 93 if(res < 0) perror("shmctl IPC_SET"); 94 } 95 96 #ifndef __FreeBSD__ // FreeBSD doesn't implement SHM_LOCK 97 if(rtapi_is_realtime()) 98 { 99 /* ensure the segment is locked */ 100 res = shmctl(shmem->id, SHM_LOCK, NULL); 101 if(res < 0) perror("shmctl IPC_LOCK"); 102 103 res = shmctl(shmem->id, IPC_STAT, &stat); 104 if(res < 0) perror("shmctl IPC_STAT"); 105 if((stat.shm_perm.mode & SHM_LOCKED) != SHM_LOCKED) 106 rtapi_print_msg(RTAPI_MSG_ERR, 107 "shared memory segment not locked as requested\n"); 108 } 109 #endif 110 #endif 111 112 /* and map it into process space */ 113 shmem->mem = shmat(shmem->id, 0, 0); 114 if ((ssize_t) (shmem->mem) == -1) { 115 rtapi_print_msg(RTAPI_MSG_ERR, "rtapi_shmem_new failed due to shmat()\n"); 116 return -errno; 117 } 118 119 long pagesize = sysconf(_SC_PAGESIZE); 120 /* touch every page */ 121 for(size_t off = 0; off < size; off += pagesize) 122 { 123 volatile char i = ((char*)shmem->mem)[off]; 124 (void)i; 125 } 126 127 /* label as a valid shmem structure */ 128 shmem->magic = SHMEM_MAGIC; 129 /* fill in the other fields */ 130 shmem->size = size; 131 shmem->key = key; 132 shmem->count = 1; 133 134 /* return handle to the caller */ 135 return i; 136 } 137 138 139 int rtapi_shmem_getptr(int handle, void **ptr) 140 { 141 rtapi_shmem_handle *shmem; 142 if(handle < 0 || handle >= MAX_SHM) 143 return -EINVAL; 144 145 shmem = &shmem_array[handle]; 146 147 /* validate shmem handle */ 148 if (shmem->magic != SHMEM_MAGIC) 149 return -EINVAL; 150 151 /* pass memory address back to caller */ 152 *ptr = shmem->mem; 153 return 0; 154 } 155 156 157 int rtapi_shmem_delete(int handle, int module_id) 158 { 159 struct shmid_ds d; 160 int r1, r2; 161 rtapi_shmem_handle *shmem; 162 163 if(handle < 0 || handle >= MAX_SHM) 164 return -EINVAL; 165 166 shmem = &shmem_array[handle]; 167 168 /* validate shmem handle */ 169 if (shmem->magic != SHMEM_MAGIC) 170 return -EINVAL; 171 172 shmem->count --; 173 if(shmem->count) return 0; 174 175 /* unmap the shared memory */ 176 r1 = shmdt(shmem->mem); 177 178 /* destroy the shared memory */ 179 r2 = shmctl(shmem->id, IPC_STAT, &d); 180 if (r2 != 0) 181 rtapi_print_msg(RTAPI_MSG_ERR, "shmctl(%d, IPC_STAT, ...): %s\n", shmem->id, strerror(errno)); 182 183 if(r2 == 0 && d.shm_nattch == 0) { 184 r2 = shmctl(shmem->id, IPC_RMID, &d); 185 if (r2 != 0) 186 rtapi_print_msg(RTAPI_MSG_ERR, "shmctl(%d, IPC_RMID, ...): %s\n", shmem->id, strerror(errno)); 187 } 188 189 /* free the shmem structure */ 190 shmem->magic = 0; 191 192 if ((r1 != 0) || (r2 != 0)) 193 return -EINVAL; 194 return 0; 195 } 196 197 198 199 200 void default_rtapi_msg_handler(msg_level_t level, const char *fmt, va_list ap); 201 202 static rtapi_msg_handler_t rtapi_msg_handler = default_rtapi_msg_handler; 203 204 rtapi_msg_handler_t rtapi_get_msg_handler(void) { 205 return rtapi_msg_handler; 206 } 207 208 void rtapi_set_msg_handler(rtapi_msg_handler_t handler) { 209 if(handler == NULL) rtapi_msg_handler = default_rtapi_msg_handler; 210 else rtapi_msg_handler = handler; 211 } 212 213 214 void rtapi_print(const char *fmt, ...) 215 { 216 va_list args; 217 218 va_start(args, fmt); 219 rtapi_msg_handler(RTAPI_MSG_ALL, fmt, args); 220 va_end(args); 221 } 222 223 224 void rtapi_print_msg(msg_level_t level, const char *fmt, ...) 225 { 226 va_list args; 227 228 if ((level <= msg_level) && (msg_level != RTAPI_MSG_NONE)) { 229 va_start(args, fmt); 230 rtapi_msg_handler(level, fmt, args); 231 va_end(args); 232 } 233 } 234 235 int rtapi_snprintf(char *buffer, unsigned long int size, const char *msg, ...) { 236 va_list args; 237 int result; 238 239 va_start(args, msg); 240 /* call the normal library vnsprintf() */ 241 result = vsnprintf(buffer, size, msg, args); 242 va_end(args); 243 return result; 244 } 245 246 int rtapi_vsnprintf(char *buffer, unsigned long int size, const char *fmt, 247 va_list args) { 248 return vsnprintf(buffer, size, fmt, args); 249 } 250 251 int rtapi_set_msg_level(int level) { 252 msg_level = level; 253 return 0; 254 } 255 256 int rtapi_get_msg_level() { 257 return msg_level; 258 } 259 260 #if defined(__i386) || defined(__amd64) 261 #define rdtscll(val) ((val) = __builtin_ia32_rdtsc()) 262 #else 263 #define rdtscll(val) ((val) = rtapi_get_time()) 264 #endif 265 266 long long rtapi_get_clocks(void) 267 { 268 long long int retval; 269 270 rdtscll(retval); 271 return retval; 272 } 273 274 typedef struct { 275 rtapi_mutex_t mutex; 276 int uuid; 277 } uuid_data_t; 278 279 #define UUID_KEY 0x48484c34 /* key for UUID for simulator */ 280 281 static int uuid_mem_id = 0; 282 int rtapi_init(const char *modname) 283 { 284 static uuid_data_t* uuid_data = 0; 285 const static int uuid_id = 0; 286 287 static char* uuid_shmem_base = 0; 288 int retval,id; 289 void *uuid_mem; 290 291 uuid_mem_id = rtapi_shmem_new(UUID_KEY,uuid_id,sizeof(uuid_data_t)); 292 if (uuid_mem_id < 0) { 293 rtapi_print_msg(RTAPI_MSG_ERR, 294 "rtapi_init: could not open shared memory for uuid\n"); 295 rtapi_exit(uuid_id); 296 return -EINVAL; 297 } 298 retval = rtapi_shmem_getptr(uuid_mem_id,&uuid_mem); 299 if (retval < 0) { 300 rtapi_print_msg(RTAPI_MSG_ERR, 301 "rtapi_init: could not access shared memory for uuid\n"); 302 rtapi_exit(uuid_id); 303 return -EINVAL; 304 } 305 if (uuid_shmem_base == 0) { 306 uuid_shmem_base = (char *) uuid_mem; 307 uuid_data = (uuid_data_t *) uuid_mem; 308 } 309 rtapi_mutex_get(&uuid_data->mutex); 310 uuid_data->uuid++; 311 id = uuid_data->uuid; 312 rtapi_mutex_give(&uuid_data->mutex); 313 314 return id; 315 } 316 317 int rtapi_exit(int module_id) 318 { 319 rtapi_shmem_delete(uuid_mem_id, module_id); 320 return 0; 321 } 322 323 int rtapi_is_kernelspace() { return 0; } 324 static int _rtapi_is_realtime = -1; 325 #ifdef __linux__ 326 static int detect_preempt_rt() { 327 struct utsname u; 328 int crit1, crit2 = 0; 329 FILE *fd; 330 331 uname(&u); 332 crit1 = strcasestr (u.version, "PREEMPT RT") != 0; 333 334 if ((fd = fopen("/sys/kernel/realtime","r")) != NULL) { 335 int flag; 336 crit2 = ((fscanf(fd, "%d", &flag) == 1) && (flag == 1)); 337 fclose(fd); 338 } 339 340 return crit1 && crit2; 341 } 342 #else 343 static int detect_preempt_rt() { 344 return 0; 345 } 346 #endif 347 #ifdef USPACE_RTAI 348 static int detect_rtai() { 349 struct utsname u; 350 uname(&u); 351 return strcasestr (u.release, "-rtai") != 0; 352 } 353 #else 354 static int detect_rtai() { 355 return 0; 356 } 357 #endif 358 #ifdef USPACE_XENOMAI 359 static int detect_xenomai() { 360 struct utsname u; 361 uname(&u); 362 return strcasestr (u.release, "-xenomai") != 0; 363 } 364 #else 365 static int detect_xenomai() { 366 return 0; 367 } 368 #endif 369 static int detect_env_override() { 370 char *p = getenv("LINUXCNC_FORCE_REALTIME"); 371 return p != NULL && atoi(p) != 0; 372 } 373 374 static int detect_realtime() { 375 struct stat st; 376 if ((stat(EMC2_BIN_DIR "/rtapi_app", &st) < 0) 377 || st.st_uid != 0 || !(st.st_mode & S_ISUID)) 378 return 0; 379 return detect_env_override() || detect_preempt_rt() || detect_rtai() || detect_xenomai(); 380 } 381 382 int rtapi_is_realtime() { 383 if(_rtapi_is_realtime == -1) _rtapi_is_realtime = detect_realtime(); 384 return _rtapi_is_realtime; 385 } 386 387 /* Like clock_nanosleep, except that an optional 'estimate of now' parameter may 388 * optionally be passed in. This is a very slight optimization for platforms 389 * where rtapi_clock_nanosleep is implemented in terms of nanosleep, because it 390 * can avoid an additional clock_gettime syscall. 391 */ 392 static int rtapi_clock_nanosleep(clockid_t clock_id, int flags, 393 const struct timespec *prequest, struct timespec *remain, 394 const struct timespec *pnow) 395 { 396 #if defined(HAVE_CLOCK_NANOSLEEP) 397 return clock_nanosleep(clock_id, flags, prequest, remain); 398 #else 399 if(flags == 0) 400 return nanosleep(prequest, remain); 401 if(flags != TIMER_ABSTIME) 402 { 403 errno = EINVAL; 404 return -1; 405 } 406 struct timespec now; 407 if(!pnow) 408 { 409 int res = clock_gettime(clock_id, &now); 410 if(res < 0) return res; 411 pnow = &now; 412 } 413 #undef timespecsub 414 #define timespecsub(tvp, uvp, vvp) \ 415 do { \ 416 (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ 417 (vvp)->tv_nsec = (tvp)->tv_nsec - (uvp)->tv_nsec; \ 418 if ((vvp)->tv_nsec < 0) { \ 419 (vvp)->tv_sec--; \ 420 (vvp)->tv_nsec += 1000000000; \ 421 } \ 422 } while (0) 423 struct timespec request; 424 timespecsub(prequest, pnow, &request); 425 return nanosleep(&request, remain); 426 #endif 427 }