Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
/*
 * Copyright (c) 2021 Stephanos Ioannidis <root@stephanos.io>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/*
 * @file Newlib thread-safety lock test
 *
 * This file contains a set of tests to verify that the newlib retargetable
 * locking interface is functional and the internal newlib locks function as
 * intended.
 */

#include <zephyr/kernel.h>
#include <zephyr/ztest.h>

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <envlock.h>

#define STACK_SIZE	(512 + CONFIG_TEST_EXTRA_STACK_SIZE)

#ifdef CONFIG_USERSPACE
#define THREAD_OPT	(K_USER | K_INHERIT_PERMS)
#else
#define THREAD_OPT	(0)
#endif /* CONFIG_USERSPACE */

static struct k_thread tdata;
static K_THREAD_STACK_DEFINE(tstack, STACK_SIZE);

/* Newlib internal lock functions */
extern void __sfp_lock_acquire(void);
extern void __sfp_lock_release(void);
extern void __sinit_lock_acquire(void);
extern void __sinit_lock_release(void);
extern void __tz_lock(void);
extern void __tz_unlock(void);

/* Static locks */
extern struct k_mutex __lock___sinit_recursive_mutex;
extern struct k_mutex __lock___sfp_recursive_mutex;
extern struct k_mutex __lock___atexit_recursive_mutex;
extern struct k_mutex __lock___malloc_recursive_mutex;
extern struct k_mutex __lock___env_recursive_mutex;
extern struct k_sem __lock___at_quick_exit_mutex;
extern struct k_sem __lock___tz_mutex;
extern struct k_sem __lock___dd_hash_mutex;
extern struct k_sem __lock___arc4random_mutex;

/**
 * @brief Test retargetable locking non-recursive (semaphore) interface
 *
 * This test verifies that a non-recursive lock (semaphore) can be dynamically
 * created, acquired, released and closed through the retargetable locking
 * interface.
 */
ZTEST(newlib_thread_safety_locks, test_retargetable_lock_sem)
{
	_LOCK_T lock = NULL;

	/* Dynamically allocate and initialise a new lock */
	__retarget_lock_init(&lock);
	zassert_not_null(lock, "non-recursive lock init failed");

	/* Acquire lock and verify acquisition */
	__retarget_lock_acquire(lock);
	zassert_equal(__retarget_lock_try_acquire(lock), 0,
		      "non-recursive lock acquisition failed");

	/* Release lock and verify release */
	__retarget_lock_release(lock);
	zassert_not_equal(__retarget_lock_try_acquire(lock), 0,
			  "non-recursive lock release failed");

	/* Close and deallocate lock */
	__retarget_lock_close(lock);
}

static void retargetable_lock_mutex_thread_acq(void *p1, void *p2, void *p3)
{
	_LOCK_T lock = p1;
	int ret;

	/*
	 * Attempt to lock the recursive lock from child thread and verify
	 * that it fails.
	 */
	ret = __retarget_lock_try_acquire_recursive(lock);
	zassert_equal(ret, 0, "recursive lock acquisition failed");
}

static void retargetable_lock_mutex_thread_rel(void *p1, void *p2, void *p3)
{
	_LOCK_T lock = p1;
	int ret;

	/*
	 * Attempt to lock the recursive lock from child thread and verify
	 * that it fails.
	 */
	ret = __retarget_lock_try_acquire_recursive(lock);
	zassert_not_equal(ret, 0, "recursive lock release failed");
}

/**
 * @brief Test retargetable locking recursive (mutex) interface
 *
 * This test verifies that a recursive lock (mutex) can be dynamically created,
 * acquired, released, and closed through the retargetable locking interface.
 */
ZTEST(newlib_thread_safety_locks, test_retargetable_lock_mutex)
{
	_LOCK_T lock = NULL;
	k_tid_t tid;

	/* Dynamically allocate and initialise a new lock */
	__retarget_lock_init_recursive(&lock);
	zassert_not_null(lock, "recursive lock init failed");

	/* Acquire lock from parent thread */
	__retarget_lock_acquire_recursive(lock);

	/* Spawn a lock acquisition check thread and wait for exit */
	tid = k_thread_create(&tdata, tstack, STACK_SIZE,
			      retargetable_lock_mutex_thread_acq, lock,
			      NULL, NULL, K_PRIO_PREEMPT(0), THREAD_OPT,
			      K_NO_WAIT);

	k_thread_join(tid, K_FOREVER);

	/* Release lock from parent thread */
	__retarget_lock_release_recursive(lock);

	/* Spawn a lock release check thread and wait for exit */
	tid = k_thread_create(&tdata, tstack, STACK_SIZE,
			      retargetable_lock_mutex_thread_rel, lock,
			      NULL, NULL, K_PRIO_PREEMPT(0), THREAD_OPT,
			      K_NO_WAIT);

	k_thread_join(tid, K_FOREVER);

	/* Close and deallocate lock */
	__retarget_lock_close_recursive(lock);
}

static void sinit_lock_thread_acq(void *p1, void *p2, void *p3)
{
	int ret;

	/*
	 * Attempt to lock the sinit mutex from child thread using
	 * retargetable locking interface. This operation should fail if the
	 * __sinit_lock_acquire() implementation internally uses the
	 * retargetable locking interface.
	 */
	ret = __retarget_lock_try_acquire_recursive(
			(_LOCK_T)&__lock___sinit_recursive_mutex);

	zassert_equal(ret, 0, "__sinit_lock_acquire() is not using "
			      "retargetable locking interface");
}

static void sinit_lock_thread_rel(void *p1, void *p2, void *p3)
{
	int ret;

	/*
	 * Attempt to lock the sinit mutex from child thread using
	 * retargetable locking interface. This operation should succeed if the
	 * __sinit_lock_release() implementation internally uses the
	 * retargetable locking interface.
	 */
	ret = __retarget_lock_try_acquire_recursive(
			(_LOCK_T)&__lock___sinit_recursive_mutex);

	zassert_not_equal(ret, 0, "__sinit_lock_release() is not using "
				  "retargetable locking interface");

	/* Release sinit lock */
	__retarget_lock_release_recursive(
		(_LOCK_T)&__lock___sinit_recursive_mutex);
}

/**
 * @brief Test sinit lock functions
 *
 * This test calls the __sinit_lock_acquire() and __sinit_lock_release()
 * functions to verify that sinit lock is functional and its implementation
 * is provided by the retargetable locking interface.
 */
ZTEST(newlib_thread_safety_locks, test_sinit_lock)
{
	k_tid_t tid;

	/* Lock the sinit mutex from parent thread */
	__sinit_lock_acquire();

	/* Spawn a lock check thread and wait for exit */
	tid = k_thread_create(&tdata, tstack, STACK_SIZE,
			      sinit_lock_thread_acq, NULL, NULL, NULL,
			      K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);

	k_thread_join(tid, K_FOREVER);

	/* Unlock the sinit mutex from parent thread */
	__sinit_lock_release();

	/* Spawn an unlock check thread and wait for exit */
	tid = k_thread_create(&tdata, tstack, STACK_SIZE,
			      sinit_lock_thread_rel, NULL, NULL, NULL,
			      K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);

	k_thread_join(tid, K_FOREVER);
}

static void sfp_lock_thread_acq(void *p1, void *p2, void *p3)
{
	int ret;

	/*
	 * Attempt to lock the sfp mutex from child thread using retargetable
	 * locking interface. This operation should fail if the
	 * __sfp_lock_acquire() implementation internally uses the retargetable
	 * locking interface.
	 */
	ret = __retarget_lock_try_acquire_recursive(
			(_LOCK_T)&__lock___sfp_recursive_mutex);

	zassert_equal(ret, 0, "__sfp_lock_acquire() is not using "
			      "retargetable locking interface");
}

static void sfp_lock_thread_rel(void *p1, void *p2, void *p3)
{
	int ret;

	/*
	 * Attempt to lock the sfp mutex from child thread using retargetable
	 * locking interface. This operation should succeed if the
	 * __sfp_lock_release() implementation internally uses the retargetable
	 * locking interface.
	 */
	ret = __retarget_lock_try_acquire_recursive(
			(_LOCK_T)&__lock___sfp_recursive_mutex);

	zassert_not_equal(ret, 0, "__sfp_lock_release() is not using "
				  "retargetable locking interface");

	/* Release sfp lock */
	__retarget_lock_release_recursive(
		(_LOCK_T)&__lock___sfp_recursive_mutex);
}

/**
 * @brief Test sfp lock functions
 *
 * This test calls the __sfp_lock_acquire() and __sfp_lock_release() functions
 * to verify that sfp lock is functional and its implementation is provided by
 * the retargetable locking interface.
 */
ZTEST(newlib_thread_safety_locks, test_sfp_lock)
{
	k_tid_t tid;

	/* Lock the sfp mutex from parent thread */
	__sfp_lock_acquire();

	/* Spawn a lock check thread and wait for exit */
	tid = k_thread_create(&tdata, tstack, STACK_SIZE,
			      sfp_lock_thread_acq, NULL, NULL, NULL,
			      K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);

	k_thread_join(tid, K_FOREVER);

	/* Unlock the sfp mutex from parent thread */
	__sfp_lock_release();

	/* Spawn an unlock check thread and wait for exit */
	tid = k_thread_create(&tdata, tstack, STACK_SIZE,
			      sfp_lock_thread_rel, NULL, NULL, NULL,
			      K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);

	k_thread_join(tid, K_FOREVER);
}

static void malloc_lock_thread_lock(void *p1, void *p2, void *p3)
{
	int ret;

	/*
	 * Attempt to lock the malloc mutex from child thread using
	 * retargetable locking interface. This operation should fail if the
	 * __malloc_lock() implementation internally uses the retargetable
	 * locking interface.
	 */
	ret = __retarget_lock_try_acquire_recursive(
			(_LOCK_T)&__lock___malloc_recursive_mutex);

	zassert_equal(ret, 0, "__malloc_lock() is not using retargetable "
			      "locking interface");
}

static void malloc_lock_thread_unlock(void *p1, void *p2, void *p3)
{
	int ret;

	/*
	 * Attempt to lock the malloc mutex from child thread using
	 * retargetable locking interface. This operation should succeed if the
	 * __malloc_unlock() implementation internally uses the retargetable
	 * locking interface.
	 */
	ret = __retarget_lock_try_acquire_recursive(
			(_LOCK_T)&__lock___malloc_recursive_mutex);

	zassert_not_equal(ret, 0, "__malloc_unlock() is not using "
				  "retargetable locking interface");

	/* Release malloc lock */
	__retarget_lock_release_recursive(
		(_LOCK_T)&__lock___malloc_recursive_mutex);
}

/**
 * @brief Test malloc lock functions
 *
 * This test calls the __malloc_lock() and __malloc_unlock() functions to
 * verify that malloc lock is functional and its implementation is provided by
 * the retargetable locking interface.
 */
ZTEST(newlib_thread_safety_locks, test_malloc_lock)
{
	k_tid_t tid;

	/* Lock the malloc mutex from parent thread */
	__malloc_lock(_REENT);

	/* Spawn a lock check thread and wait for exit */
	tid = k_thread_create(&tdata, tstack, STACK_SIZE,
			      malloc_lock_thread_lock, NULL, NULL, NULL,
			      K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);

	k_thread_join(tid, K_FOREVER);

	/* Unlock the malloc mutex from parent thread */
	__malloc_unlock(_REENT);

	/* Spawn an unlock check thread and wait for exit */
	tid = k_thread_create(&tdata, tstack, STACK_SIZE,
			      malloc_lock_thread_unlock, NULL, NULL, NULL,
			      K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);

	k_thread_join(tid, K_FOREVER);
}

static void env_lock_thread_lock(void *p1, void *p2, void *p3)
{
	int ret;

	/*
	 * Attempt to lock the env mutex from child thread using
	 * retargetable locking interface. This operation should fail if the
	 * __env_lock() implementation internally uses the retargetable
	 * locking interface.
	 */
	ret = __retarget_lock_try_acquire_recursive(
			(_LOCK_T)&__lock___env_recursive_mutex);

	zassert_equal(ret, 0, "__env_lock() is not using retargetable "
			      "locking interface");
}

static void env_lock_thread_unlock(void *p1, void *p2, void *p3)
{
	int ret;

	/*
	 * Attempt to lock the env mutex from child thread using
	 * retargetable locking interface. This operation should succeed if the
	 * __env_unlock() implementation internally uses the retargetable
	 * locking interface.
	 */
	ret = __retarget_lock_try_acquire_recursive(
			(_LOCK_T)&__lock___env_recursive_mutex);

	zassert_not_equal(ret, 0, "__env_unlock() is not using "
				  "retargetable locking interface");

	/* Release env lock */
	__retarget_lock_release_recursive(
		(_LOCK_T)&__lock___env_recursive_mutex);
}

/**
 * @brief Test env lock functions
 *
 * This test calls the __env_lock() and __env_unlock() functions to verify
 * that env lock is functional and its implementation is provided by the
 * retargetable locking interface.
 */
ZTEST(newlib_thread_safety_locks, test_env_lock)
{
	k_tid_t tid;

	/* Lock the env mutex from parent thread */
	__env_lock(_REENT);

	/* Spawn a lock check thread and wait for exit */
	tid = k_thread_create(&tdata, tstack, STACK_SIZE,
			      env_lock_thread_lock, NULL, NULL, NULL,
			      K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);

	k_thread_join(tid, K_FOREVER);

	/* Unlock the env mutex from parent thread */
	__env_unlock(_REENT);

	/* Spawn an unlock check thread and wait for exit */
	tid = k_thread_create(&tdata, tstack, STACK_SIZE,
			      env_lock_thread_unlock, NULL, NULL, NULL,
			      K_PRIO_PREEMPT(0), THREAD_OPT, K_NO_WAIT);

	k_thread_join(tid, K_FOREVER);
}

/**
 * @brief Test tz lock functions
 *
 * This test calls the __tz_lock() and __tz_unlock() functions to verify that
 * tz lock is functional and its implementation is provided by the retargetable
 * locking interface.
 */
ZTEST(newlib_thread_safety_locks, test_tz_lock)
{
	/* Lock the tz semaphore */
	__tz_lock();

	/* Attempt to acquire lock and verify failure */
	zassert_equal(
		__retarget_lock_try_acquire((_LOCK_T)&__lock___tz_mutex), 0,
		"__tz_lock() is not using retargetable locking interface");

	/* Unlock the tz semaphore */
	__tz_unlock();

	/* Attempt to acquire lock and verify success */
	zassert_not_equal(
		__retarget_lock_try_acquire((_LOCK_T)&__lock___tz_mutex), 0,
		"__tz_unlock() is not using retargetable locking interface");

	/* Clean up */
	__retarget_lock_release((_LOCK_T)&__lock___tz_mutex);
}

void *newlib_thread_safety_locks_setup(void)
{
#ifdef CONFIG_USERSPACE
	k_thread_access_grant(k_current_get(), &tdata, &tstack);
#endif /* CONFIG_USERSPACE */
	return NULL;
}
ZTEST_SUITE(newlib_thread_safety_locks, NULL, newlib_thread_safety_locks_setup, NULL, NULL, NULL);