Chinese translated version of Documentation/arch/arm/kernel_user_helpers.rst

If you have any comment or update to the content, please contact the
original document maintainer directly.  However, if you have a problem
communicating in English you can also ask the Chinese maintainer for
help.  Contact the Chinese maintainer if this translation is outdated
or if there is a problem with the translation.

Maintainer: Nicolas Pitre <nicolas.pitre@linaro.org>
		Dave Martin <dave.martin@linaro.org>
Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
---------------------------------------------------------------------
Documentation/arch/arm/kernel_user_helpers.rst 的中文翻譯

如果想評論或更新本文的內容，請直接聯繫原文檔的維護者。如果你使用英文
交流有困難的話，也可以向中文版維護者求助。如果本翻譯更新不及時或者翻
譯存在問題，請聯繫中文版維護者。
英文版維護者： Nicolas Pitre <nicolas.pitre@linaro.org>
		Dave Martin <dave.martin@linaro.org>
中文版維護者： 傅煒 Fu Wei <tekkamanninja@gmail.com>
中文版翻譯者： 傅煒 Fu Wei <tekkamanninja@gmail.com>
中文版校譯者： 宋冬生 Dongsheng Song <dongshneg.song@gmail.com>
		傅煒 Fu Wei <tekkamanninja@gmail.com>


以下爲正文
---------------------------------------------------------------------
內核提供的用戶空間輔助代碼
=========================

在內核內存空間的固定地址處，有一個由內核提供並可從用戶空間訪問的代碼
段。它用於向用戶空間提供因在許多 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 flag = 如果 r0 == 0 則置 1，如果 r0 != 0 則清零。

被篡改的寄存器:

  r3, ip, flags

定義:

  僅在 *ptr 爲 oldval 時原子保存 newval 於 *ptr 中。
  如果 *ptr 被改變，則返回值爲零，否則爲非零值。
  如果 *ptr 被改變，則 C flag 也會被置 1，以實現調用代碼中的彙編
  優化。

使用範例:

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 flag = 如果 r0 == 0 則置 1，如果 r0 != 0 則清零。

被篡改的寄存器:

  r3, lr, flags

定義:

  僅在 *ptr 等於 *oldval 指向的 64 位值時，原子保存 *newval
  指向的 64 位值於 *ptr 中。如果 *ptr 被改變，則返回值爲零，
  否則爲非零值。

  如果 *ptr 被改變，則 C flag 也會被置 1，以實現調用代碼中的彙編
  優化。

使用範例:

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 開始）。

