核心提供的使用者輔助程式

這些是核心提供的使用者程式碼片段,可以從使用者空間以核心記憶體中的固定地址訪問。它用於為使用者空間提供一些需要核心幫助的操作,因為許多 ARM CPU 中未實現本機功能和/或指令。 這樣做的目的是使此程式碼直接在使用者模式下執行以獲得最佳效率,但它與核心對應部分過於緊密,無法留給使用者庫。 事實上,這段程式碼甚至可能因 CPU 而異,具體取決於可用的指令集,或者它是否是 SMP 系統。 換句話說,核心保留根據需要更改此程式碼的權利,恕不另行通知。 只有此處記錄的入口點及其結果才能保證穩定。

這與完整的 VDSO 實現不同(但不排除),但是 VDSO 會阻止一些帶有常量的彙編技巧,這些技巧可以有效地分支到這些程式碼段。 並且由於這些程式碼段僅在使用幾個週期後返回使用者程式碼,因此 VDSO 間接遠呼叫的開銷將為此類最小操作增加可測量的開銷。

使用者空間應繞過這些輔助程式,並在為具有必要本機支援的最新處理器進行最佳化時,內聯實現這些內容(無論是編譯器直接發出的程式碼,還是庫呼叫的實現的一部分),但前提是生成的二進位制檔案由於對其他事物使用類似的本機指令而已經與早期 ARM 處理器不相容。 換句話說,不要僅僅為了不使用這些核心輔助程式而使二進位制檔案無法在早期處理器上執行,如果你的編譯程式碼不會將新指令用於其他目的。

新的輔助程式可能會隨著時間的推移而新增,因此較舊的核心可能會缺少較新核心中存在的一些輔助程式。 因此,程式必須在假定可以安全呼叫任何特定輔助程式之前,檢查 __kuser_helper_version 的值(請參見下文)。 理想情況下,此檢查應僅在程序啟動時執行一次,如果程序在其上執行的核心版本未提供所需的輔助程式,則應儘早中止執行。

kuser_helper_version

位置:0xffff0ffc

參考宣告

extern int32_t __kuser_helper_version;

定義

此欄位包含正在執行的核心實現的輔助程式的數量。 使用者空間可以讀取此值以確定特定輔助程式的可用性。

用法示例

#define __kuser_helper_version (*(int32_t *)0xffff0ffc)

void check_kuser_version(void)
{
      if (__kuser_helper_version < 2) {
              fprintf(stderr, "can't do atomic operations, kernel too old\n");
              abort();
      }
}

註釋

使用者空間可以假定此欄位的值在任何單個程序的生命週期內永遠不會改變。 這意味著可以在庫的初始化或程式的啟動階段讀取一次此欄位。

kuser_get_tls

位置:0xffff0fe0

參考原型

void * __kuser_get_tls(void);

輸入

lr = 返回地址

輸出

r0 = TLS 值

被破壞的暫存器

定義

獲取先前透過 __ARM_NR_set_tls 系統呼叫設定的 TLS 值。

用法示例

typedef void * (__kuser_get_tls_t)(void);
#define __kuser_get_tls (*(__kuser_get_tls_t *)0xffff0fe0)

void foo()
{
      void *tls = __kuser_get_tls();
      printf("TLS = %p\n", tls);
}

註釋

  • 僅當 __kuser_helper_version >= 1 時有效(來自核心版本 2.6.12)。

kuser_cmpxchg

位置:0xffff0fc0

參考原型

int __kuser_cmpxchg(int32_t oldval, int32_t newval, volatile int32_t *ptr);

輸入

r0 = oldval r1 = newval r2 = ptr lr = 返回地址

輸出

r0 = 成功程式碼(零或非零)C 標誌 = 如果 r0 == 0 則設定,如果 r0 != 0 則清除

被破壞的暫存器

r3、ip、flags

定義

僅當 *ptr 等於 oldval 時,才以原子方式將 newval 儲存在 *ptr 中。 如果 *ptr 已更改,則返回零,如果未發生交換,則返回非零。 如果 *ptr 已更改,也會設定 C 標誌,以便在呼叫程式碼中進行彙編最佳化。

用法示例

typedef int (__kuser_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
#define __kuser_cmpxchg (*(__kuser_cmpxchg_t *)0xffff0fc0)

int atomic_add(volatile int *ptr, int val)
{
      int old, new;

      do {
              old = *ptr;
              new = old + val;
      } while(__kuser_cmpxchg(old, new, ptr));

      return new;
}

註釋

  • 此例程已根據需要包含記憶體屏障。

  • 僅當 __kuser_helper_version >= 2 時有效(來自核心版本 2.6.12)。

kuser_memory_barrier

位置:0xffff0fa0

參考原型

void __kuser_memory_barrier(void);

輸入

lr = 返回地址

輸出

被破壞的暫存器

定義

應用任何需要的記憶體屏障,以保持與手動修改的資料和 __kuser_cmpxchg 用法的一致性。

用法示例

typedef void (__kuser_dmb_t)(void);
#define __kuser_dmb (*(__kuser_dmb_t *)0xffff0fa0)

註釋

  • 僅當 __kuser_helper_version >= 3 時有效(來自核心版本 2.6.15)。

kuser_cmpxchg64

位置:0xffff0f60

參考原型

int __kuser_cmpxchg64(const int64_t *oldval,
                      const int64_t *newval,
                      volatile int64_t *ptr);

輸入

r0 = 指向 oldval 的指標 r1 = 指向 newval 的指標 r2 = 指向目標值的指標 lr = 返回地址

輸出

r0 = 成功程式碼(零或非零)C 標誌 = 如果 r0 == 0 則設定,如果 r0 != 0 則清除

被破壞的暫存器

r3, lr, flags

定義

僅當 *ptr 等於 *oldval 指向的 64 位值時,才以原子方式將 *newval 指向的 64 位值儲存在 *ptr 中。 如果 *ptr 已更改,則返回零,如果未發生交換,則返回非零。

如果 *ptr 已更改,也會設定 C 標誌,以便在呼叫程式碼中進行彙編最佳化。

用法示例

typedef int (__kuser_cmpxchg64_t)(const int64_t *oldval,
                                  const int64_t *newval,
                                  volatile int64_t *ptr);
#define __kuser_cmpxchg64 (*(__kuser_cmpxchg64_t *)0xffff0f60)

int64_t atomic_add64(volatile int64_t *ptr, int64_t val)
{
      int64_t old, new;

      do {
              old = *ptr;
              new = old + val;
      } while(__kuser_cmpxchg64(&old, &new, ptr));

      return new;
}

註釋

  • 此例程已根據需要包含記憶體屏障。

  • 由於此序列的長度,它跨越了 2 個常規 kuser “槽”,因此 0xffff0f80 不用作有效的入口點。

  • 僅當 __kuser_helper_version >= 5 時有效(來自核心版本 3.1)。