1*91f16700Schasinglulu /* 2*91f16700Schasinglulu * Copyright (c) 2013-2018, Arm Limited and Contributors. All rights reserved. 3*91f16700Schasinglulu * 4*91f16700Schasinglulu * SPDX-License-Identifier: BSD-3-Clause 5*91f16700Schasinglulu */ 6*91f16700Schasinglulu 7*91f16700Schasinglulu #include <assert.h> 8*91f16700Schasinglulu #include <string.h> 9*91f16700Schasinglulu 10*91f16700Schasinglulu #include <arch_helpers.h> 11*91f16700Schasinglulu #include <lib/bakery_lock.h> 12*91f16700Schasinglulu #include <lib/el3_runtime/cpu_data.h> 13*91f16700Schasinglulu #include <plat/common/platform.h> 14*91f16700Schasinglulu 15*91f16700Schasinglulu /* 16*91f16700Schasinglulu * Functions in this file implement Bakery Algorithm for mutual exclusion with the 17*91f16700Schasinglulu * bakery lock data structures in coherent memory. 18*91f16700Schasinglulu * 19*91f16700Schasinglulu * ARM architecture offers a family of exclusive access instructions to 20*91f16700Schasinglulu * efficiently implement mutual exclusion with hardware support. However, as 21*91f16700Schasinglulu * well as depending on external hardware, the these instructions have defined 22*91f16700Schasinglulu * behavior only on certain memory types (cacheable and Normal memory in 23*91f16700Schasinglulu * particular; see ARMv8 Architecture Reference Manual section B2.10). Use cases 24*91f16700Schasinglulu * in trusted firmware are such that mutual exclusion implementation cannot 25*91f16700Schasinglulu * expect that accesses to the lock have the specific type required by the 26*91f16700Schasinglulu * architecture for these primitives to function (for example, not all 27*91f16700Schasinglulu * contenders may have address translation enabled). 28*91f16700Schasinglulu * 29*91f16700Schasinglulu * This implementation does not use mutual exclusion primitives. It expects 30*91f16700Schasinglulu * memory regions where the locks reside to be fully ordered and coherent 31*91f16700Schasinglulu * (either by disabling address translation, or by assigning proper attributes 32*91f16700Schasinglulu * when translation is enabled). 33*91f16700Schasinglulu * 34*91f16700Schasinglulu * Note that the ARM architecture guarantees single-copy atomicity for aligned 35*91f16700Schasinglulu * accesses regardless of status of address translation. 36*91f16700Schasinglulu */ 37*91f16700Schasinglulu 38*91f16700Schasinglulu #define assert_bakery_entry_valid(_entry, _bakery) do { \ 39*91f16700Schasinglulu assert((_bakery) != NULL); \ 40*91f16700Schasinglulu assert((_entry) < BAKERY_LOCK_MAX_CPUS); \ 41*91f16700Schasinglulu } while (false) 42*91f16700Schasinglulu 43*91f16700Schasinglulu /* Obtain a ticket for a given CPU */ 44*91f16700Schasinglulu static unsigned int bakery_get_ticket(bakery_lock_t *bakery, unsigned int me) 45*91f16700Schasinglulu { 46*91f16700Schasinglulu unsigned int my_ticket, their_ticket; 47*91f16700Schasinglulu unsigned int they; 48*91f16700Schasinglulu 49*91f16700Schasinglulu /* Prevent recursive acquisition */ 50*91f16700Schasinglulu assert(bakery_ticket_number(bakery->lock_data[me]) == 0U); 51*91f16700Schasinglulu 52*91f16700Schasinglulu /* 53*91f16700Schasinglulu * Flag that we're busy getting our ticket. All CPUs are iterated in the 54*91f16700Schasinglulu * order of their ordinal position to decide the maximum ticket value 55*91f16700Schasinglulu * observed so far. Our priority is set to be greater than the maximum 56*91f16700Schasinglulu * observed priority 57*91f16700Schasinglulu * 58*91f16700Schasinglulu * Note that it's possible that more than one contender gets the same 59*91f16700Schasinglulu * ticket value. That's OK as the lock is acquired based on the priority 60*91f16700Schasinglulu * value, not the ticket value alone. 61*91f16700Schasinglulu */ 62*91f16700Schasinglulu my_ticket = 0U; 63*91f16700Schasinglulu bakery->lock_data[me] = make_bakery_data(CHOOSING_TICKET, my_ticket); 64*91f16700Schasinglulu for (they = 0U; they < BAKERY_LOCK_MAX_CPUS; they++) { 65*91f16700Schasinglulu their_ticket = bakery_ticket_number(bakery->lock_data[they]); 66*91f16700Schasinglulu if (their_ticket > my_ticket) 67*91f16700Schasinglulu my_ticket = their_ticket; 68*91f16700Schasinglulu } 69*91f16700Schasinglulu 70*91f16700Schasinglulu /* 71*91f16700Schasinglulu * Compute ticket; then signal to other contenders waiting for us to 72*91f16700Schasinglulu * finish calculating our ticket value that we're done 73*91f16700Schasinglulu */ 74*91f16700Schasinglulu ++my_ticket; 75*91f16700Schasinglulu bakery->lock_data[me] = make_bakery_data(CHOSEN_TICKET, my_ticket); 76*91f16700Schasinglulu 77*91f16700Schasinglulu return my_ticket; 78*91f16700Schasinglulu } 79*91f16700Schasinglulu 80*91f16700Schasinglulu 81*91f16700Schasinglulu /* 82*91f16700Schasinglulu * Acquire bakery lock 83*91f16700Schasinglulu * 84*91f16700Schasinglulu * Contending CPUs need first obtain a non-zero ticket and then calculate 85*91f16700Schasinglulu * priority value. A contending CPU iterate over all other CPUs in the platform, 86*91f16700Schasinglulu * which may be contending for the same lock, in the order of their ordinal 87*91f16700Schasinglulu * position (CPU0, CPU1 and so on). A non-contending CPU will have its ticket 88*91f16700Schasinglulu * (and priority) value as 0. The contending CPU compares its priority with that 89*91f16700Schasinglulu * of others'. The CPU with the highest priority (lowest numerical value) 90*91f16700Schasinglulu * acquires the lock 91*91f16700Schasinglulu */ 92*91f16700Schasinglulu void bakery_lock_get(bakery_lock_t *bakery) 93*91f16700Schasinglulu { 94*91f16700Schasinglulu unsigned int they, me; 95*91f16700Schasinglulu unsigned int my_ticket, my_prio, their_ticket; 96*91f16700Schasinglulu unsigned int their_bakery_data; 97*91f16700Schasinglulu 98*91f16700Schasinglulu me = plat_my_core_pos(); 99*91f16700Schasinglulu 100*91f16700Schasinglulu assert_bakery_entry_valid(me, bakery); 101*91f16700Schasinglulu 102*91f16700Schasinglulu /* Get a ticket */ 103*91f16700Schasinglulu my_ticket = bakery_get_ticket(bakery, me); 104*91f16700Schasinglulu 105*91f16700Schasinglulu /* 106*91f16700Schasinglulu * Now that we got our ticket, compute our priority value, then compare 107*91f16700Schasinglulu * with that of others, and proceed to acquire the lock 108*91f16700Schasinglulu */ 109*91f16700Schasinglulu my_prio = bakery_get_priority(my_ticket, me); 110*91f16700Schasinglulu for (they = 0U; they < BAKERY_LOCK_MAX_CPUS; they++) { 111*91f16700Schasinglulu if (me == they) 112*91f16700Schasinglulu continue; 113*91f16700Schasinglulu 114*91f16700Schasinglulu /* Wait for the contender to get their ticket */ 115*91f16700Schasinglulu do { 116*91f16700Schasinglulu their_bakery_data = bakery->lock_data[they]; 117*91f16700Schasinglulu } while (bakery_is_choosing(their_bakery_data)); 118*91f16700Schasinglulu 119*91f16700Schasinglulu /* 120*91f16700Schasinglulu * If the other party is a contender, they'll have non-zero 121*91f16700Schasinglulu * (valid) ticket value. If they do, compare priorities 122*91f16700Schasinglulu */ 123*91f16700Schasinglulu their_ticket = bakery_ticket_number(their_bakery_data); 124*91f16700Schasinglulu if ((their_ticket != 0U) && 125*91f16700Schasinglulu (bakery_get_priority(their_ticket, they) < my_prio)) { 126*91f16700Schasinglulu /* 127*91f16700Schasinglulu * They have higher priority (lower value). Wait for 128*91f16700Schasinglulu * their ticket value to change (either release the lock 129*91f16700Schasinglulu * to have it dropped to 0; or drop and probably content 130*91f16700Schasinglulu * again for the same lock to have an even higher value) 131*91f16700Schasinglulu */ 132*91f16700Schasinglulu do { 133*91f16700Schasinglulu wfe(); 134*91f16700Schasinglulu } while (their_ticket == 135*91f16700Schasinglulu bakery_ticket_number(bakery->lock_data[they])); 136*91f16700Schasinglulu } 137*91f16700Schasinglulu } 138*91f16700Schasinglulu 139*91f16700Schasinglulu /* 140*91f16700Schasinglulu * Lock acquired. Ensure that any reads and writes from a shared 141*91f16700Schasinglulu * resource in the critical section read/write values after the lock is 142*91f16700Schasinglulu * acquired. 143*91f16700Schasinglulu */ 144*91f16700Schasinglulu dmbish(); 145*91f16700Schasinglulu } 146*91f16700Schasinglulu 147*91f16700Schasinglulu 148*91f16700Schasinglulu /* Release the lock and signal contenders */ 149*91f16700Schasinglulu void bakery_lock_release(bakery_lock_t *bakery) 150*91f16700Schasinglulu { 151*91f16700Schasinglulu unsigned int me = plat_my_core_pos(); 152*91f16700Schasinglulu 153*91f16700Schasinglulu assert_bakery_entry_valid(me, bakery); 154*91f16700Schasinglulu assert(bakery_ticket_number(bakery->lock_data[me]) != 0U); 155*91f16700Schasinglulu 156*91f16700Schasinglulu /* 157*91f16700Schasinglulu * Ensure that other observers see any stores in the critical section 158*91f16700Schasinglulu * before releasing the lock. Also ensure all loads in the critical 159*91f16700Schasinglulu * section are complete before releasing the lock. Release the lock by 160*91f16700Schasinglulu * resetting ticket. Then signal other waiting contenders. 161*91f16700Schasinglulu */ 162*91f16700Schasinglulu dmbish(); 163*91f16700Schasinglulu bakery->lock_data[me] = 0U; 164*91f16700Schasinglulu 165*91f16700Schasinglulu /* Required to ensure ordering of the following sev */ 166*91f16700Schasinglulu dsb(); 167*91f16700Schasinglulu sev(); 168*91f16700Schasinglulu } 169