code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :doc:`../../../admin-guide/security-bugs`

:譯者:

 吳想成 Wu XiangCheng <bobwxc@email.cn>
 胡皓文 Hu Haowen <src.res@email.cn>

安全缺陷
=========

Linux內核開發人員非常重視安全性。因此我們想知道何時發現了安全漏洞，以便儘快
修復和披露。請向Linux內核安全團隊報告安全漏洞。

聯絡
-----

可以通過電子郵件<security@kernel.org>聯繫Linux內核安全團隊。這是一個安全人員
的私有列表，他們將幫助驗證錯誤報告並開發和發布修復程序。如果您已經有了一個
修復，請將其包含在您的報告中，這樣可以大大加快進程。安全團隊可能會從區域維護
人員那裡獲得額外的幫助，以理解和修復安全漏洞。

與任何缺陷一樣，提供的信息越多，診斷和修復就越容易。如果您不清楚哪些信息有用，
請查看「Documentation/translations/zh_TW/admin-guide/reporting-issues.rst」中
概述的步驟。任何利用漏洞的攻擊代碼都非常有用，未經報告者同意不會對外發布，除
非已經公開。

請儘可能發送無附件的純文本電子郵件。如果所有的細節都藏在附件里，那麼就很難對
一個複雜的問題進行上下文引用的討論。把它想像成一個
:doc:`常規的補丁提交 <../process/submitting-patches>` （即使你還沒有補丁）：
描述問題和影響，列出復現步驟，然後給出一個建議的解決方案，所有這些都是純文本的。

披露和限制信息
---------------

安全列表不是公開渠道。爲此，請參見下面的協作。

一旦開發出了健壯的補丁，發布過程就開始了。對公開的缺陷的修復會立即發布。

儘管我們傾向於在未公開缺陷的修復可用時即發布補丁，但應報告者或受影響方的請求，
這可能會被推遲到發布過程開始後的7日內，如果根據缺陷的嚴重性需要更多的時間，
則可額外延長到14天。推遲發布修復的唯一有效原因是爲了適應QA的邏輯和需要發布
協調的大規模部署。

雖然可能與受信任的個人共享受限信息以開發修復，但未經報告者許可，此類信息不會
與修復程序一起發布或發布在任何其他披露渠道上。這包括但不限於原始錯誤報告和
後續討論（如有）、漏洞、CVE信息或報告者的身份。

換句話說，我們唯一感興趣的是修復缺陷。提交給安全列表的所有其他資料以及對報告
的任何後續討論，即使在解除限制之後，也將永久保密。

協調
------

對敏感缺陷（例如那些可能導致權限提升的缺陷）的修復可能需要與私有郵件列表
<linux-distros@vs.openwall.org>進行協調，以便分發供應商做好準備，在公開披露
上游補丁時發布一個已修復的內核。發行版將需要一些時間來測試建議的補丁，通常
會要求至少幾天的限制，而供應商更新發布更傾向於周二至周四。若合適，安全團隊
可以協助這種協調，或者報告者可以從一開始就包括linux發行版。在這種情況下，請
記住在電子郵件主題行前面加上「[vs]」，如linux發行版wiki中所述：
<http://oss-security.openwall.org/wiki/mailing-lists/distros#how-to-use-the-lists>。

CVE分配
--------

安全團隊通常不分配CVE，我們也不需要它們來進行報告或修復，因爲這會使過程不必
要的複雜化，並可能耽誤缺陷處理。如果報告者希望在公開披露之前分配一個CVE編號，
他們需要聯繫上述的私有linux-distros列表。當在提供補丁之前已有這樣的CVE編號時，
如報告者願意，最好在提交消息中提及它。

保密協議
---------

Linux內核安全團隊不是一個正式的機構實體，因此無法簽訂任何保密協議。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :doc:`../../../admin-guide/tainted-kernels`

:譯者:

 吳想成 Wu XiangCheng <bobwxc@email.cn>
 胡皓文 Hu Haowen <src.res@email.cn>

受汙染的內核
-------------

當發生一些在稍後調查問題時可能相關的事件時，內核會將自己標記爲「受汙染
（tainted）」的。不用太過擔心，大多數情況下運行受汙染的內核沒有問題；這些信息
主要在有人想調查某個問題時才有意義的，因爲問題的真正原因可能是導致內核受汙染
的事件。這就是爲什麼來自受汙染內核的缺陷報告常常被開發人員忽略，因此請嘗試用
未受汙染的內核重現問題。

請注意，即使在您消除導致汙染的原因（亦即卸載專有內核模塊）之後，內核仍將保持
汙染狀態，以表示內核仍然不可信。這也是爲什麼內核在注意到內部問題（「kernel
bug」）、可恢復錯誤（「kernel oops」）或不可恢復錯誤（「kernel panic」）時會列印
受汙染狀態，並將有關此的調試信息寫入日誌 ``dmesg`` 輸出。也可以通過
``/proc/`` 中的文件在運行時檢查受汙染的狀態。

BUG、Oops或Panics消息中的汙染標誌
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

在頂部以「CPU:」開頭的一行中可以找到受汙染的狀態；內核是否受到汙染和原因會顯示
在進程ID（「PID:」）和觸發事件命令的縮寫名稱（「Comm:」）之後::

	BUG: unable to handle kernel NULL pointer dereference at 0000000000000000
	Oops: 0002 [#1] SMP PTI
	CPU: 0 PID: 4424 Comm: insmod Tainted: P        W  O      4.20.0-0.rc6.fc30 #1
	Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011
	RIP: 0010:my_oops_init+0x13/0x1000 [kpanic]
	[...]

如果內核在事件發生時沒有被汙染，您將在那裡看到「Not-tainted:」；如果被汙染，那
麼它將是「Tainted:」以及字母或空格。在上面的例子中，它看起來是這樣的::

	Tainted: P        W  O

下表解釋了這些字符的含義。在本例中，由於加載了專有模塊（ ``P`` ），出現了
警告（ ``W`` ），並且加載了外部構建的模塊（ ``O`` ），所以內核早些時候受到
了汙染。要解碼其他字符，請使用下表。

解碼運行時的汙染狀態
~~~~~~~~~~~~~~~~~~~~~

在運行時，您可以通過讀取 ``cat /proc/sys/kernel/tainted`` 來查詢受汙染狀態。
如果返回 ``0`` ，則內核沒有受到汙染；任何其他數字都表示受到汙染的原因。解碼
這個數字的最簡單方法是使用腳本  ``tools/debugging/kernel-chktaint`` ，您的
發行版可能會將其作爲名爲 ``linux-tools`` 或 ``kernel-tools`` 的包的一部分提
供；如果沒有，您可以從
`git.kernel.org <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/tools/debugging/kernel-chktaint>`_
網站下載此腳本並用 ``sh kernel-chktaint`` 執行，它會在上面引用的日誌中有類似
語句的機器上列印這樣的內容::

	Kernel is Tainted for following reasons:
	 * Proprietary module was loaded (#0)
	 * Kernel issued warning (#9)
	 * Externally-built ('out-of-tree') module was loaded  (#12)
	See Documentation/admin-guide/tainted-kernels.rst in the Linux kernel or
	 https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html for
	 a more details explanation of the various taint flags.
	Raw taint value as int/string: 4609/'P        W  O     '

你也可以試著自己解碼這個數字。如果內核被汙染的原因只有一個，那麼這很簡單，
在本例中您可以通過下表找到數字。如果你需要解碼有多個原因的數字，因爲它是一
個位域（bitfield），其中每個位表示一個特定類型的汙染的存在或不存在，最好讓
前面提到的腳本來處理。但是如果您需要快速看一下，可以使用這個shell命令來檢查
設置了哪些位::

	$ for i in $(seq 18); do echo $(($i-1)) $(($(cat /proc/sys/kernel/tainted)>>($i-1)&1));done

汙染狀態代碼表
~~~~~~~~~~~~~~~

===  =====  ======  ========================================================
 位  日誌     數字  內核被汙染的原因
===  =====  ======  ========================================================
  0   G/P        1  已加載專用模塊
  1   _/F        2  模塊被強制加載
  2   _/S        4  內核運行在不合規範的系統上
  3   _/R        8  模塊被強制卸載
  4   _/M       16  處理器報告了機器檢測異常（MCE）
  5   _/B       32  引用了錯誤的頁或某些意外的頁標誌
  6   _/U       64  用戶空間應用程式請求的汙染
  7   _/D      128  內核最近死機了，即曾出現OOPS或BUG
  8   _/A      256  ACPI表被用戶覆蓋
  9   _/W      512  內核發出警告
 10   _/C     1024  已加載staging驅動程序
 11   _/I     2048  已應用平台固件缺陷的解決方案
 12   _/O     4096  已加載外部構建（「樹外」）模塊
 13   _/E     8192  已加載未簽名的模塊
 14   _/L    16384  發生軟鎖定
 15   _/K    32768  內核已實時打補丁
 16   _/X    65536  備用汙染，爲發行版定義並使用
 17   _/T   131072  內核是用結構隨機化插件構建的
===  =====  ======  ========================================================

註：字符 ``_`` 表示空白，以便於閱讀表。

汙染的更詳細解釋
~~~~~~~~~~~~~~~~~

 0)  ``G`` 加載的所有模塊都有GPL或兼容許可證， ``P`` 加載了任何專有模塊。
     沒有MODULE_LICENSE（模塊許可證）或MODULE_LICENSE未被insmod認可爲GPL
     兼容的模塊被認爲是專有的。

 1)  ``F`` 任何模塊被 ``insmod -f`` 強制加載， ``' '`` 所有模塊正常加載。

 2)  ``S`` 內核運行在不合規範的處理器或系統上：硬體已運行在不受支持的配置中，
     因此無法保證正確執行。內核將被汙染，例如：

     - 在x86上：PAE是通過intel CPU（如Pentium M）上的forcepae強制執行的，這些
       CPU不報告PAE，但可能有功能實現，SMP內核在非官方支持的SMP Athlon CPU上
       運行，MSR被暴露到用戶空間中。
     - 在arm上：在某些CPU（如Keystone 2）上運行的內核，沒有啓用某些內核特性。
     - 在arm64上：CPU之間存在不匹配的硬體特性，引導加載程序以不同的模式引導CPU。
     - 某些驅動程序正在被用在不受支持的體系結構上（例如x86_64以外的其他系統
       上的scsi/snic，非x86/x86_64/itanium上的scsi/ips，已經損壞了arm64上
       irqchip/irq-gic的固件設置…）。

 3)  ``R`` 模塊被 ``rmmod -f`` 強制卸載， ``' '`` 所有模塊都正常卸載。

 4)  ``M`` 任何處理器報告了機器檢測異常， ``' '`` 未發生機器檢測異常。

 5)  ``B`` 頁面釋放函數發現錯誤的頁面引用或某些意外的頁面標誌。這表示硬體問題
     或內核錯誤；日誌中應該有其他信息指示發生此汙染的原因。

 6)  ``U`` 用戶或用戶應用程式特意請求設置受汙染標誌，否則應爲 ``' '`` 。

 7)  ``D`` 內核最近死機了，即出現了OOPS或BUG。

 8)  ``A`` ACPI表被重寫。

 9)  ``W`` 內核之前已發出過警告（儘管有些警告可能會設置更具體的汙染標誌）。

 10) ``C`` 已加載staging驅動程序。

 11) ``I`` 內核正在處理平台固件（BIOS或類似軟體）中的嚴重錯誤。

 12) ``O`` 已加載外部構建（「樹外」）模塊。

 13) ``E`` 在支持模塊簽名的內核中加載了未簽名的模塊。

 14) ``L`` 系統上先前發生過軟鎖定。

 15) ``K`` 內核已經實時打了補丁。

 16) ``X`` 備用汙染，由Linux發行版定義和使用。

 17) ``T`` 內核構建時使用了randstruct插件，它可以有意生成非常不尋常的內核結構
     布局（甚至是性能病態的布局），這在調試時非常有用。於構建時設置。

repo_name: angery/linux


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: Documentation/admin-guide/unicode.rst

:譯者:

 吳想成 Wu XiangCheng <bobwxc@email.cn>
 胡皓文 Hu Haowen <src.res@email.cn>

Unicode（統一碼）支持
======================

	（英文版）上次更新：2005-01-17，版本號 1.4

此文檔由H. Peter Anvin <unicode@lanana.org>管理，是Linux註冊名稱與編號管理局
（Linux Assigned Names And Numbers Authority，LANANA）項目的一部分。
現行版本請見：

	http://www.lanana.org/docs/unicode/admin-guide/unicode.rst

簡介
-----

Linux內核代碼已被重寫以使用Unicode來將字符映射到字體。下載一個Unicode到字體
（Unicode-to-font）表，八位字符集與UTF-8模式都將改用此字體來顯示。

這微妙地改變了八位字符表的語義。現在的四個字符表是：

=============== =============================== ================
映射代號        映射名稱                        Escape代碼 (G0)
=============== =============================== ================
LAT1_MAP        Latin-1 (ISO 8859-1)            ESC ( B
GRAF_MAP        DEC VT100 pseudographics        ESC ( 0
IBMPC_MAP       IBM code page 437               ESC ( U
USER_MAP        User defined                    ESC ( K
=============== =============================== ================

特別是 ESC ( U 不再是「直通字體」，因爲字體可能與IBM字符集完全不同。
例如，即使加載了一個Latin-1字體，也允許使用塊圖形（block graphics）。

請注意，儘管這些代碼與ISO 2022類似，但這些代碼及其用途都與ISO 2022不匹配；
Linux有兩個八位代碼（G0和G1），而ISO 2022有四個七位代碼（G0-G3）。

根據Unicode標準/ISO 10646，U+F000到U+F8FF被保留用於作業系統範圍內的分配
（Unicode標準將其稱爲「團體區域（Corporate Zone）」，因爲這對於Linux是不準確
的，所以我們稱之爲「Linux區域」）。選擇U+F000作爲起點，因爲它允許直接映射
區域以2的大倍數開始（以防需要1024或2048個字符的字體）。這就留下U+E000到
U+EFFF作爲最終用戶區。

[v1.2]：Unicodes範圍從U+F000到U+F7FF已經被硬編碼爲直接映射到加載的字體，
繞過了翻譯表。用戶定義的映射現在默認爲U+F000到U+F0FF，模擬前述行爲。實際上，
此範圍可能較短；例如，vgacon只能處理256字符（U+F000..U+F0FF）或512字符
（U+F000..U+F1FF）字體。

Linux 區域中定義的實際字符
---------------------------

此外，還定義了Unicode 1.1.4中不存在的以下字符；這些字符由DEC VT圖形映射使用。
[v1.2]此用法已過時，不應再使用；請參見下文。

====== ======================================
U+F800 DEC VT GRAPHICS HORIZONTAL LINE SCAN 1
U+F801 DEC VT GRAPHICS HORIZONTAL LINE SCAN 3
U+F803 DEC VT GRAPHICS HORIZONTAL LINE SCAN 7
U+F804 DEC VT GRAPHICS HORIZONTAL LINE SCAN 9
====== ======================================

DEC VT220使用6x10字符矩陣，這些字符在DEC VT圖形字符集中形成一個平滑的過渡。
我省略了掃描5行，因爲它也被用作塊圖形字符，因此被編碼爲U+2500 FORMS LIGHT
HORIZONTAL。

[v1.3]：這些字符已正式添加到Unicode 3.2.0中；它們在U+23BA、U+23BB、U+23BC、
U+23BD處添加。Linux現在使用新值。

[v1.2]：添加了以下字符來表示常見的鍵盤符號，這些符號不太可能被添加到Unicode
中，因爲它們非常討厭地取決於特定供應商。當然，這是糟糕設計的一個好例子。

====== ======================================
U+F810 KEYBOARD SYMBOL FLYING FLAG
U+F811 KEYBOARD SYMBOL PULLDOWN MENU
U+F812 KEYBOARD SYMBOL OPEN APPLE
U+F813 KEYBOARD SYMBOL SOLID APPLE
====== ======================================

克林貢（Klingon）語支持
------------------------

1996年，Linux是世界上第一個添加對人工語言克林貢支持的作業系統，克林貢是由
Marc Okrand爲《星際迷航》電視連續劇創造的。這種編碼後來被徵募Unicode註冊表
（ConScript Unicode Registry，CSUR）採用，並建議（但最終被拒絕）納入Unicode
平面一。不過，它仍然是Linux區域中的Linux/CSUR私有分配。

這種編碼已經得到克林貢語言研究所（Klingon Language Institute）的認可。
有關更多信息，請聯繫他們：

	http://www.kli.org/

由於Linux CZ開頭部分的字符大多是dingbats/symbols/forms類型，而且這是一種
語言，因此根據標準Unicode慣例，我將它放置在16單元的邊界上。

.. note::

  這個範圍現在由徵募Unicode註冊表正式管理。規範性引用文件爲：

	https://www.evertype.com/standards/csur/klingon.html

克林貢語有一個26個字符的字母表，一個10位數的位置數字書寫系統，從左到右
，從上到下書寫。

克林貢字母的幾種字形已經被提出。但是由於這組符號看起來始終是一致的，只有實際
的形狀不同，因此按照標準Unicode慣例，這些差異被認爲是字體變體。

======	=======================================================
U+F8D0	KLINGON LETTER A
U+F8D1	KLINGON LETTER B
U+F8D2	KLINGON LETTER CH
U+F8D3	KLINGON LETTER D
U+F8D4	KLINGON LETTER E
U+F8D5	KLINGON LETTER GH
U+F8D6	KLINGON LETTER H
U+F8D7	KLINGON LETTER I
U+F8D8	KLINGON LETTER J
U+F8D9	KLINGON LETTER L
U+F8DA	KLINGON LETTER M
U+F8DB	KLINGON LETTER N
U+F8DC	KLINGON LETTER NG
U+F8DD	KLINGON LETTER O
U+F8DE	KLINGON LETTER P
U+F8DF	KLINGON LETTER Q
	- Written <q> in standard Okrand Latin transliteration
U+F8E0	KLINGON LETTER QH
	- Written <Q> in standard Okrand Latin transliteration
U+F8E1	KLINGON LETTER R
U+F8E2	KLINGON LETTER S
U+F8E3	KLINGON LETTER T
U+F8E4	KLINGON LETTER TLH
U+F8E5	KLINGON LETTER U
U+F8E6	KLINGON LETTER V
U+F8E7	KLINGON LETTER W
U+F8E8	KLINGON LETTER Y
U+F8E9	KLINGON LETTER GLOTTAL STOP

U+F8F0	KLINGON DIGIT ZERO
U+F8F1	KLINGON DIGIT ONE
U+F8F2	KLINGON DIGIT TWO
U+F8F3	KLINGON DIGIT THREE
U+F8F4	KLINGON DIGIT FOUR
U+F8F5	KLINGON DIGIT FIVE
U+F8F6	KLINGON DIGIT SIX
U+F8F7	KLINGON DIGIT SEVEN
U+F8F8	KLINGON DIGIT EIGHT
U+F8F9	KLINGON DIGIT NINE

U+F8FD	KLINGON COMMA
U+F8FE	KLINGON FULL STOP
U+F8FF	KLINGON SYMBOL FOR EMPIRE
======	=======================================================

其他虛構和人工字母
-------------------

自從分配了克林貢Linux Unicode塊之後，John Cowan <jcowan@reutershealth.com>
和 Michael Everson <everson@evertype.com> 建立了一個虛構和人工字母的註冊表。
徵募Unicode註冊表請訪問：

	https://www.evertype.com/standards/csur/

所使用的範圍位於最終用戶區域的低端，因此無法進行規範化分配，但建議希望對虛構
字母進行編碼的人員使用這些代碼，以實現互操作性。對於克林貢語，CSUR採用了Linux
編碼。CSUR的人正在推動將Tengwar和Cirth添加到Unicode平面一；將克林貢添加到
Unicode平面一被拒絕，因此上述編碼仍然是官方的。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :ref:`Documentation/arm64/amu.rst <amu_index>`

Translator: Bailu Lin <bailu.lin@vivo.com>
            Hu Haowen <src.res@email.cn>

==================================
AArch64 Linux 中擴展的活動監控單元
==================================

作者: Ionela Voinescu <ionela.voinescu@arm.com>

日期: 2019-09-10

本文檔簡要描述了 AArch64 Linux 支持的活動監控單元的規範。

架構總述
--------

活動監控是 ARMv8.4 CPU 架構引入的一個可選擴展特性。

活動監控單元(在每個 CPU 中實現)爲系統管理提供了性能計數器。既可以通
過系統寄存器的方式訪問計數器，同時也支持外部內存映射的方式訪問計數器。

AMUv1 架構實現了一個由4個固定的64位事件計數器組成的計數器組。

  - CPU 周期計數器：同 CPU 的頻率增長
  - 常量計數器：同固定的系統時鐘頻率增長
  - 淘汰指令計數器: 同每次架構指令執行增長
  - 內存停頓周期計數器：計算由在時鐘域內的最後一級緩存中未命中而引起
    的指令調度停頓周期數

當處於 WFI 或者 WFE 狀態時，計數器不會增長。

AMU 架構提供了一個高達16位的事件計數器空間，未來新的 AMU 版本中可能
用它來實現新增的事件計數器。

另外，AMUv1 實現了一個多達16個64位輔助事件計數器的計數器組。

冷復位時所有的計數器會清零。

基本支持
--------

內核可以安全地運行在支持 AMU 和不支持 AMU 的 CPU 組合中。
因此，當配置 CONFIG_ARM64_AMU_EXTN 後我們無條件使能後續
(secondary or hotplugged) CPU 檢測和使用這個特性。

當在 CPU 上檢測到該特性時，我們會標記爲特性可用但是不能保證計數器的功能，
僅表明有擴展屬性。

固件(代碼運行在高異常級別，例如 arm-tf )需支持以下功能：

 - 提供低異常級別(EL2 和 EL1)訪問 AMU 寄存器的能力。
 - 使能計數器。如果未使能，它的值應爲 0。
 - 在從電源關閉狀態啓動 CPU 前或後保存或者恢復計數器。

當使用使能了該特性的內核啓動但固件損壞時，訪問計數器寄存器可能會遭遇
panic 或者死鎖。即使未發現這些症狀，計數器寄存器返回的數據結果並不一
定能反映真實情況。通常，計數器會返回 0，表明他們未被使能。

如果固件沒有提供適當的支持最好關閉 CONFIG_ARM64_AMU_EXTN。
值得注意的是，出於安全原因，不要繞過 AMUSERRENR_EL0 設置而捕獲從
EL0(用戶空間) 訪問 EL1(內核空間)。 因此，固件應該確保訪問 AMU寄存器
不會困在 EL2或EL3。

AMUv1 的固定計數器可以通過如下系統寄存器訪問：

 - SYS_AMEVCNTR0_CORE_EL0
 - SYS_AMEVCNTR0_CONST_EL0
 - SYS_AMEVCNTR0_INST_RET_EL0
 - SYS_AMEVCNTR0_MEM_STALL_EL0

特定輔助計數器可以通過 SYS_AMEVCNTR1_EL0(n) 訪問，其中n介於0到15。

詳細信息定義在目錄：arch/arm64/include/asm/sysreg.h。

用戶空間訪問
------------

由於以下原因，當前禁止從用戶空間訪問 AMU 的寄存器：

  - 安全因數：可能會暴露處於安全模式執行的代碼信息。
  - 意願：AMU 是用於系統管理的。

同樣，該功能對用戶空間不可見。

虛擬化
------

由於以下原因，當前禁止從 KVM 客戶端的用戶空間(EL0)和內核空間(EL1)
訪問 AMU 的寄存器：

  - 安全因數：可能會暴露給其他客戶端或主機端執行的代碼信息。

任何試圖訪問 AMU 寄存器的行爲都會觸發一個註冊在客戶端的未定義異常。


code:
SPDX-License-Identifier: GPL-2.0

Chinese translated version of Documentation/arm64/booting.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.

M:	Will Deacon <will.deacon@arm.com>
zh_CN:	Fu Wei <wefu@redhat.com>
zh_TW:	Hu Haowen <src.res@email.cn>
C:	55f058e7574c3615dea4615573a19bdb258696c6
---------------------------------------------------------------------
Documentation/arm64/booting.rst 的中文翻譯

如果想評論或更新本文的內容，請直接聯繫原文檔的維護者。如果你使用英文
交流有困難的話，也可以向中文版維護者求助。如果本翻譯更新不及時或者翻
譯存在問題，請聯繫中文版維護者。

英文版維護者： Will Deacon <will.deacon@arm.com>
中文版維護者： 傅煒  Fu Wei <wefu@redhat.com>
中文版翻譯者： 傅煒  Fu Wei <wefu@redhat.com>
中文版校譯者： 傅煒  Fu Wei <wefu@redhat.com>
繁體中文版校譯者： 胡皓文  Hu Haowen <src.res@email.cn>
本文翻譯提交時的 Git 檢出點爲： 55f058e7574c3615dea4615573a19bdb258696c6

以下爲正文
---------------------------------------------------------------------
			啓動 AArch64 Linux
			==================

作者: Will Deacon <will.deacon@arm.com>
日期: 2012 年 09 月 07 日

本文檔基於 Russell King 的 ARM 啓動文檔，且適用於所有公開發布的
AArch64 Linux 內核代碼。

AArch64 異常模型由多個異常級（EL0 - EL3）組成，對於 EL0 和 EL1 異常級
有對應的安全和非安全模式。EL2 是系統管理級，且僅存在於非安全模式下。
EL3 是最高特權級，且僅存在於安全模式下。

基於本文檔的目的，我們將簡單地使用『引導裝載程序』（『boot loader』）
這個術語來定義在將控制權交給 Linux 內核前 CPU 上執行的所有軟體。
這可能包含安全監控和系統管理代碼，或者它可能只是一些用於準備最小啓動
環境的指令。

基本上，引導裝載程序（至少）應實現以下操作：

1、設置和初始化 RAM
2、設置設備樹數據
3、解壓內核映像
4、調用內核映像

1、設置和初始化 RAM
-----------------

必要性: 強制

引導裝載程序應該找到並初始化系統中所有內核用於保持系統變量數據的 RAM。
這個操作的執行方式因設備而異。（它可能使用內部算法來自動定位和計算所有
RAM，或可能使用對這個設備已知的 RAM 信息，還可能是引導裝載程序設計者
想到的任何合適的方法。）

2、設置設備樹數據
---------------

必要性: 強制

設備樹數據塊（dtb）必須 8 字節對齊，且大小不能超過 2MB。由於設備樹
數據塊將在使能緩存的情況下以 2MB 粒度被映射，故其不能被置於必須以特定
屬性映射的2M區域內。

註： v4.2 之前的版本同時要求設備樹數據塊被置於從內核映像以下
text_offset 字節處算起第一個 512MB 內。

3、解壓內核映像
-------------

必要性: 可選

AArch64 內核當前沒有提供自解壓代碼，因此如果使用了壓縮內核映像文件
（比如 Image.gz），則需要通過引導裝載程序（使用 gzip 等）來進行解壓。
若引導裝載程序沒有實現這個功能，就要使用非壓縮內核映像文件。

4、調用內核映像
-------------

必要性: 強制

已解壓的內核映像包含一個 64 字節的頭，內容如下：

  u32 code0;			/* 可執行代碼 */
  u32 code1;			/* 可執行代碼 */
  u64 text_offset;		/* 映像裝載偏移，小端模式 */
  u64 image_size;		/* 映像實際大小, 小端模式 */
  u64 flags;			/* 內核旗標, 小端模式 *
  u64 res2	= 0;		/* 保留 */
  u64 res3	= 0;		/* 保留 */
  u64 res4	= 0;		/* 保留 */
  u32 magic	= 0x644d5241;	/* 魔數, 小端, "ARM\x64" */
  u32 res5;			/* 保留 （用於 PE COFF 偏移） */

映像頭注釋：

- 自 v3.17 起，除非另有說明，所有域都是小端模式。

- code0/code1 負責跳轉到 stext.

- 當通過 EFI 啓動時， 最初 code0/code1 被跳過。
  res5 是到 PE 文件頭的偏移，而 PE 文件頭含有 EFI 的啓動入口點
  （efi_stub_entry）。當 stub 代碼完成了它的使命，它會跳轉到 code0
  繼續正常的啓動流程。

- v3.17 之前，未明確指定 text_offset 的字節序。此時，image_size 爲零，
  且 text_offset 依照內核字節序爲 0x80000。
  當 image_size 非零，text_offset 爲小端模式且是有效值，應被引導加載
  程序使用。當 image_size 爲零，text_offset 可假定爲 0x80000。

- flags 域 (v3.17 引入) 爲 64 位小端模式，其編碼如下：
  位 0: 	內核字節序。 1 表示大端模式，0 表示小端模式。
  位 1-2:	內核頁大小。
			0 - 未指定。
			1 - 4K
			2 - 16K
			3 - 64K
  位 3:		內核物理位置
			0 - 2MB 對齊基址應儘量靠近內存起始處，因爲
			    其基址以下的內存無法通過線性映射訪問
			1 - 2MB 對齊基址可以在物理內存的任意位置
  位 4-63:	保留。

- 當 image_size 爲零時，引導裝載程序應試圖在內核映像末尾之後儘可能
  多地保留空閒內存供內核直接使用。對內存空間的需求量因所選定的內核
  特性而異, 並無實際限制。

內核映像必須被放置在任意一個可用系統內存 2MB 對齊基址的 text_offset
字節處，並從該處被調用。2MB 對齊基址和內核映像起始地址之間的區域對於
內核來說沒有特殊意義，且可能被用於其他目的。
從映像起始地址算起，最少必須準備 image_size 字節的空閒內存供內核使用。
註： v4.6 之前的版本無法使用內核映像物理偏移以下的內存，所以當時建議
將映像儘量放置在靠近系統內存起始的地方。

任何提供給內核的內存（甚至在映像起始地址之前），若未從內核中標記爲保留
(如在設備樹（dtb）的 memreserve 區域），都將被認爲對內核是可用。

在跳轉入內核前，必須符合以下狀態：

- 停止所有 DMA 設備，這樣內存數據就不會因爲虛假網絡包或磁碟數據而
  被破壞。這可能可以節省你許多的調試時間。

- 主 CPU 通用寄存器設置
  x0 = 系統 RAM 中設備樹數據塊（dtb）的物理地址。
  x1 = 0 (保留，將來可能使用)
  x2 = 0 (保留，將來可能使用)
  x3 = 0 (保留，將來可能使用)

- CPU 模式
  所有形式的中斷必須在 PSTATE.DAIF 中被屏蔽（Debug、SError、IRQ
  和 FIQ）。
  CPU 必須處於 EL2（推薦，可訪問虛擬化擴展）或非安全 EL1 模式下。

- 高速緩存、MMU
  MMU 必須關閉。
  指令緩存開啓或關閉皆可。
  已載入的內核映像的相應內存區必須被清理，以達到緩存一致性點（PoC）。
  當存在系統緩存或其他使能緩存的一致性主控器時，通常需使用虛擬地址
  維護其緩存，而非 set/way 操作。
  遵從通過虛擬地址操作維護構架緩存的系統緩存必須被配置，並可以被使能。
  而不通過虛擬地址操作維護構架緩存的系統緩存（不推薦），必須被配置且
  禁用。

  *譯者註：對於 PoC 以及緩存相關內容，請參考 ARMv8 構架參考手冊
   ARM DDI 0487A

- 架構計時器
  CNTFRQ 必須設定爲計時器的頻率，且 CNTVOFF 必須設定爲對所有 CPU
  都一致的值。如果在 EL1 模式下進入內核，則 CNTHCTL_EL2 中的
  EL1PCTEN (bit 0) 必須置位。

- 一致性
  通過內核啓動的所有 CPU 在內核入口地址上必須處於相同的一致性域中。
  這可能要根據具體實現來定義初始化過程，以使能每個CPU上對維護操作的
  接收。

- 系統寄存器
  在進入內核映像的異常級中，所有構架中可寫的系統寄存器必須通過軟體
  在一個更高的異常級別下初始化，以防止在 未知 狀態下運行。

  對於擁有 GICv3 中斷控制器並以 v3 模式運行的系統：
  - 如果 EL3 存在：
    ICC_SRE_EL3.Enable (位 3) 必須初始化爲 0b1。
    ICC_SRE_EL3.SRE (位 0) 必須初始化爲 0b1。
  - 若內核運行在 EL1：
    ICC_SRE_EL2.Enable (位 3) 必須初始化爲 0b1。
    ICC_SRE_EL2.SRE (位 0) 必須初始化爲 0b1。
  - 設備樹（DT）或 ACPI 表必須描述一個 GICv3 中斷控制器。

  對於擁有 GICv3 中斷控制器並以兼容（v2）模式運行的系統：
  - 如果 EL3 存在：
    ICC_SRE_EL3.SRE (位 0) 必須初始化爲 0b0。
  - 若內核運行在 EL1：
    ICC_SRE_EL2.SRE (位 0) 必須初始化爲 0b0。
  - 設備樹（DT）或 ACPI 表必須描述一個 GICv2 中斷控制器。

以上對於 CPU 模式、高速緩存、MMU、架構計時器、一致性、系統寄存器的
必要條件描述適用於所有 CPU。所有 CPU 必須在同一異常級別跳入內核。

引導裝載程序必須在每個 CPU 處於以下狀態時跳入內核入口：

- 主 CPU 必須直接跳入內核映像的第一條指令。通過此 CPU 傳遞的設備樹
  數據塊必須在每個 CPU 節點中包含一個 『enable-method』 屬性，所
  支持的 enable-method 請見下文。

  引導裝載程序必須生成這些設備樹屬性，並在跳入內核入口之前將其插入
  數據塊。

- enable-method 爲 「spin-table」 的 CPU 必須在它們的 CPU
  節點中包含一個 『cpu-release-addr』 屬性。這個屬性標識了一個
  64 位自然對齊且初始化爲零的內存位置。

  這些 CPU 必須在內存保留區（通過設備樹中的 /memreserve/ 域傳遞
  給內核）中自旋於內核之外，輪詢它們的 cpu-release-addr 位置（必須
  包含在保留區中）。可通過插入 wfe 指令來降低忙循環開銷，而主 CPU 將
  發出 sev 指令。當對 cpu-release-addr 所指位置的讀取操作返回非零值
  時，CPU 必須跳入此值所指向的地址。此值爲一個單獨的 64 位小端值，
  因此 CPU 須在跳轉前將所讀取的值轉換爲其本身的端模式。

- enable-method 爲 「psci」 的 CPU 保持在內核外（比如，在
  memory 節點中描述爲內核空間的內存區外，或在通過設備樹 /memreserve/
  域中描述爲內核保留區的空間中）。內核將會發起在 ARM 文檔（編號
  ARM DEN 0022A：用於 ARM 上的電源狀態協調接口系統軟體）中描述的
  CPU_ON 調用來將 CPU 帶入內核。

  *譯者注: ARM DEN 0022A 已更新到 ARM DEN 0022C。

  設備樹必須包含一個 『psci』 節點，請參考以下文檔：
  Documentation/devicetree/bindings/arm/psci.yaml

- 輔助 CPU 通用寄存器設置
  x0 = 0 (保留，將來可能使用)
  x1 = 0 (保留，將來可能使用)
  x2 = 0 (保留，將來可能使用)
  x3 = 0 (保留，將來可能使用)


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :ref:`Documentation/arm64/elf_hwcaps.rst <elf_hwcaps_index>`

Translator: Bailu Lin <bailu.lin@vivo.com>
            Hu Haowen <src.res@email.cn>

================
ARM64 ELF hwcaps
================

這篇文檔描述了 arm64 ELF hwcaps 的用法和語義。

1. 簡介
-------

有些硬體或軟體功能僅在某些 CPU 實現上和/或在具體某個內核配置上可用，但
對於處於 EL0 的用戶空間代碼沒有可用的架構發現機制。內核通過在輔助向量表
公開一組稱爲 hwcaps 的標誌而把這些功能暴露給用戶空間。

用戶空間軟體可以通過獲取輔助向量的 AT_HWCAP 或 AT_HWCAP2 條目來測試功能，
並測試是否設置了相關標誌，例如::

	bool floating_point_is_present(void)
	{
		unsigned long hwcaps = getauxval(AT_HWCAP);
		if (hwcaps & HWCAP_FP)
			return true;

		return false;
	}

如果軟體依賴於 hwcap 描述的功能，在嘗試使用該功能前則應檢查相關的 hwcap
標誌以驗證該功能是否存在。

不能通過其他方式探查這些功能。當一個功能不可用時，嘗試使用它可能導致不可
預測的行爲，並且無法保證能確切的知道該功能不可用，例如 SIGILL。

2. Hwcaps 的說明
----------------

大多數 hwcaps 旨在說明通過架構 ID 寄存器(處於 EL0 的用戶空間代碼無法訪問)
描述的功能的存在。這些 hwcap 通過 ID 寄存器欄位定義，並且應根據 ARM 體系
結構參考手冊（ARM ARM）中定義的欄位來解釋說明。

這些 hwcaps 以下面的形式描述::

    idreg.field == val 表示有某個功能。

當 idreg.field 中有 val 時，hwcaps 表示 ARM ARM 定義的功能是有效的，但是
並不是說要完全和 val 相等，也不是說 idreg.field 描述的其他功能就是缺失的。

其他 hwcaps 可能表明無法僅由 ID 寄存器描述的功能的存在。這些 hwcaps 可能
沒有被 ID 寄存器描述，需要參考其他文檔。

3. AT_HWCAP 中揭示的 hwcaps
---------------------------

HWCAP_FP
    ID_AA64PFR0_EL1.FP == 0b0000 表示有此功能。

HWCAP_ASIMD
    ID_AA64PFR0_EL1.AdvSIMD == 0b0000 表示有此功能。

HWCAP_EVTSTRM
    通用計時器頻率配置爲大約100KHz以生成事件。

HWCAP_AES
    ID_AA64ISAR0_EL1.AES == 0b0001 表示有此功能。

HWCAP_PMULL
    ID_AA64ISAR0_EL1.AES == 0b0010 表示有此功能。

HWCAP_SHA1
    ID_AA64ISAR0_EL1.SHA1 == 0b0001 表示有此功能。

HWCAP_SHA2
    ID_AA64ISAR0_EL1.SHA2 == 0b0001 表示有此功能。

HWCAP_CRC32
    ID_AA64ISAR0_EL1.CRC32 == 0b0001 表示有此功能。

HWCAP_ATOMICS
    ID_AA64ISAR0_EL1.Atomic == 0b0010 表示有此功能。

HWCAP_FPHP
    ID_AA64PFR0_EL1.FP == 0b0001 表示有此功能。

HWCAP_ASIMDHP
    ID_AA64PFR0_EL1.AdvSIMD == 0b0001 表示有此功能。

HWCAP_CPUID
    根據 Documentation/arm64/cpu-feature-registers.rst 描述，EL0 可以訪問
    某些 ID 寄存器。

    這些 ID 寄存器可能表示功能的可用性。

HWCAP_ASIMDRDM
    ID_AA64ISAR0_EL1.RDM == 0b0001 表示有此功能。

HWCAP_JSCVT
    ID_AA64ISAR1_EL1.JSCVT == 0b0001 表示有此功能。

HWCAP_FCMA
    ID_AA64ISAR1_EL1.FCMA == 0b0001 表示有此功能。

HWCAP_LRCPC
    ID_AA64ISAR1_EL1.LRCPC == 0b0001 表示有此功能。

HWCAP_DCPOP
    ID_AA64ISAR1_EL1.DPB == 0b0001 表示有此功能。

HWCAP_SHA3
    ID_AA64ISAR0_EL1.SHA3 == 0b0001 表示有此功能。

HWCAP_SM3
    ID_AA64ISAR0_EL1.SM3 == 0b0001 表示有此功能。

HWCAP_SM4
    ID_AA64ISAR0_EL1.SM4 == 0b0001 表示有此功能。

HWCAP_ASIMDDP
    ID_AA64ISAR0_EL1.DP == 0b0001 表示有此功能。

HWCAP_SHA512
    ID_AA64ISAR0_EL1.SHA2 == 0b0010 表示有此功能。

HWCAP_SVE
    ID_AA64PFR0_EL1.SVE == 0b0001 表示有此功能。

HWCAP_ASIMDFHM
    ID_AA64ISAR0_EL1.FHM == 0b0001 表示有此功能。

HWCAP_DIT
    ID_AA64PFR0_EL1.DIT == 0b0001 表示有此功能。

HWCAP_USCAT
    ID_AA64MMFR2_EL1.AT == 0b0001 表示有此功能。

HWCAP_ILRCPC
    ID_AA64ISAR1_EL1.LRCPC == 0b0010 表示有此功能。

HWCAP_FLAGM
    ID_AA64ISAR0_EL1.TS == 0b0001 表示有此功能。

HWCAP_SSBS
    ID_AA64PFR1_EL1.SSBS == 0b0010 表示有此功能。

HWCAP_SB
    ID_AA64ISAR1_EL1.SB == 0b0001 表示有此功能。

HWCAP_PACA
    如 Documentation/arm64/pointer-authentication.rst 所描述，
    ID_AA64ISAR1_EL1.APA == 0b0001 或 ID_AA64ISAR1_EL1.API == 0b0001
    表示有此功能。

HWCAP_PACG
    如 Documentation/arm64/pointer-authentication.rst 所描述，
    ID_AA64ISAR1_EL1.GPA == 0b0001 或 ID_AA64ISAR1_EL1.GPI == 0b0001
    表示有此功能。

HWCAP2_DCPODP

    ID_AA64ISAR1_EL1.DPB == 0b0010 表示有此功能。

HWCAP2_SVE2

    ID_AA64ZFR0_EL1.SVEVer == 0b0001 表示有此功能。

HWCAP2_SVEAES

    ID_AA64ZFR0_EL1.AES == 0b0001 表示有此功能。

HWCAP2_SVEPMULL

    ID_AA64ZFR0_EL1.AES == 0b0010 表示有此功能。

HWCAP2_SVEBITPERM

    ID_AA64ZFR0_EL1.BitPerm == 0b0001 表示有此功能。

HWCAP2_SVESHA3

    ID_AA64ZFR0_EL1.SHA3 == 0b0001 表示有此功能。

HWCAP2_SVESM4

    ID_AA64ZFR0_EL1.SM4 == 0b0001 表示有此功能。

HWCAP2_FLAGM2

    ID_AA64ISAR0_EL1.TS == 0b0010 表示有此功能。

HWCAP2_FRINT

    ID_AA64ISAR1_EL1.FRINTTS == 0b0001 表示有此功能。

HWCAP2_SVEI8MM

    ID_AA64ZFR0_EL1.I8MM == 0b0001 表示有此功能。

HWCAP2_SVEF32MM

    ID_AA64ZFR0_EL1.F32MM == 0b0001 表示有此功能。

HWCAP2_SVEF64MM

    ID_AA64ZFR0_EL1.F64MM == 0b0001 表示有此功能。

HWCAP2_SVEBF16

    ID_AA64ZFR0_EL1.BF16 == 0b0001 表示有此功能。

HWCAP2_I8MM

    ID_AA64ISAR1_EL1.I8MM == 0b0001 表示有此功能。

HWCAP2_BF16

    ID_AA64ISAR1_EL1.BF16 == 0b0001 表示有此功能。

HWCAP2_DGH

    ID_AA64ISAR1_EL1.DGH == 0b0001 表示有此功能。

HWCAP2_RNG

    ID_AA64ISAR0_EL1.RNDR == 0b0001 表示有此功能。

HWCAP2_BTI

    ID_AA64PFR0_EL1.BT == 0b0001 表示有此功能。

4. 未使用的 AT_HWCAP 位
-----------------------

爲了與用戶空間交互，內核保證 AT_HWCAP 的第62、63位將始終返回0。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :ref:`Documentation/arm64/hugetlbpage.rst <hugetlbpage_index>`

Translator: Bailu Lin <bailu.lin@vivo.com>
            Hu Haowen <src.res@email.cn>

=====================
ARM64中的 HugeTLBpage
=====================

大頁依靠有效利用 TLBs 來提高地址翻譯的性能。這取決於以下
兩點 -

  - 大頁的大小
  - TLBs 支持的條目大小

ARM64 接口支持2種大頁方式。

1) pud/pmd 級別的塊映射
-----------------------

這是常規大頁，他們的 pmd 或 pud 頁面表條目指向一個內存塊。
不管 TLB 中支持的條目大小如何，塊映射可以減少翻譯大頁地址
所需遍歷的頁表深度。

2) 使用連續位
-------------

架構中轉換頁表條目(D4.5.3, ARM DDI 0487C.a)中提供一個連續
位告訴 MMU 這個條目是一個連續條目集的一員，它可以被緩存在單
個 TLB 條目中。

在 Linux 中連續位用來增加 pmd 和 pte(最後一級)級別映射的大
小。受支持的連續頁表條目數量因頁面大小和頁表級別而異。

支持以下大頁尺寸配置 -

  ====== ========   ====    ========    ===
  -      CONT PTE    PMD    CONT PMD    PUD
  ====== ========   ====    ========    ===
  4K:         64K     2M         32M     1G
  16K:         2M    32M          1G
  64K:         2M   512M         16G
  ====== ========   ====    ========    ===


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :ref:`Documentation/arm64/index.rst <arm64_index>`
:Translator: Bailu Lin <bailu.lin@vivo.com>
             Hu Haowen <src.res@email.cn>

.. _tw_arm64_index:

==========
ARM64 架構
==========

.. toctree::
    :maxdepth: 2

    amu
    hugetlbpage
    perf
    elf_hwcaps


code:
SPDX-License-Identifier: GPL-2.0

Chinese translated version of Documentation/arm64/legacy_instructions.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: Punit Agrawal <punit.agrawal@arm.com>
            Suzuki K. Poulose <suzuki.poulose@arm.com>
Chinese maintainer: Fu Wei <wefu@redhat.com>
Traditional Chinese maintainer: Hu Haowen <src.res@email.cn>
---------------------------------------------------------------------
Documentation/arm64/legacy_instructions.rst 的中文翻譯

如果想評論或更新本文的內容，請直接聯繫原文檔的維護者。如果你使用英文
交流有困難的話，也可以向中文版維護者求助。如果本翻譯更新不及時或者翻
譯存在問題，請聯繫中文版維護者。

本文翻譯提交時的 Git 檢出點爲： bc465aa9d045feb0e13b4a8f32cc33c1943f62d6

英文版維護者： Punit Agrawal <punit.agrawal@arm.com>
            Suzuki K. Poulose <suzuki.poulose@arm.com>
中文版維護者： 傅煒  Fu Wei <wefu@redhat.com>
中文版翻譯者： 傅煒  Fu Wei <wefu@redhat.com>
中文版校譯者： 傅煒  Fu Wei <wefu@redhat.com>
繁體中文版校譯者：胡皓文  Hu Haowen <src.res@email.cn>

以下爲正文
---------------------------------------------------------------------
Linux 內核在 arm64 上的移植提供了一個基礎框架，以支持構架中正在被淘汰或已廢棄指令的模擬執行。
這個基礎框架的代碼使用未定義指令鉤子（hooks）來支持模擬。如果指令存在，它也允許在硬體中啓用該指令。

模擬模式可通過寫 sysctl 節點（/proc/sys/abi）來控制。
不同的執行方式及 sysctl 節點的相應值，解釋如下：

* Undef（未定義）
  值： 0
  產生未定義指令終止異常。它是那些構架中已廢棄的指令，如 SWP，的默認處理方式。

* Emulate（模擬）
  值： 1
  使用軟體模擬方式。爲解決軟體遷移問題，這種模擬指令模式的使用是被跟蹤的，並會發出速率限制警告。
  它是那些構架中正在被淘汰的指令，如 CP15 barriers（隔離指令），的默認處理方式。

* Hardware Execution（硬體執行）
  值： 2
  雖然標記爲正在被淘汰，但一些實現可能提供硬體執行這些指令的使能/禁用操作。
  使用硬體執行一般會有更好的性能，但將無法收集運行時對正被淘汰指令的使用統計數據。

默認執行模式依賴於指令在構架中狀態。正在被淘汰的指令應該以模擬（Emulate）作爲默認模式，
而已廢棄的指令必須默認使用未定義（Undef）模式

注意：指令模擬可能無法應對所有情況。更多詳情請參考單獨的指令注釋。

受支持的遺留指令
-------------
* SWP{B}
節點: /proc/sys/abi/swp
狀態: 已廢棄
默認執行方式: Undef (0)

* CP15 Barriers
節點: /proc/sys/abi/cp15_barrier
狀態: 正被淘汰，不推薦使用
默認執行方式: Emulate (1)

* SETEND
節點: /proc/sys/abi/setend
狀態: 正被淘汰，不推薦使用
默認執行方式: Emulate (1)*
註：爲了使能這個特性，系統中的所有 CPU 必須在 EL0 支持混合字節序。
如果一個新的 CPU （不支持混合字節序） 在使能這個特性後被熱插入系統，
在應用中可能會出現不可預期的結果。


code:
SPDX-License-Identifier: GPL-2.0

Chinese translated version of Documentation/arm64/memory.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: Catalin Marinas <catalin.marinas@arm.com>
Chinese maintainer: Fu Wei <wefu@redhat.com>
Traditional Chinese maintainer: Hu Haowen <src.res@email.cn>
---------------------------------------------------------------------
Documentation/arm64/memory.rst 的中文翻譯

如果想評論或更新本文的內容，請直接聯繫原文檔的維護者。如果你使用英文
交流有困難的話，也可以向中文版維護者求助。如果本翻譯更新不及時或者翻
譯存在問題，請聯繫中文版維護者。

本文翻譯提交時的 Git 檢出點爲： bc465aa9d045feb0e13b4a8f32cc33c1943f62d6

英文版維護者： Catalin Marinas <catalin.marinas@arm.com>
中文版維護者： 傅煒  Fu Wei <wefu@redhat.com>
中文版翻譯者： 傅煒  Fu Wei <wefu@redhat.com>
中文版校譯者： 傅煒  Fu Wei <wefu@redhat.com>
繁體中文版校譯者： 胡皓文  Hu Haowen <src.res@email.cn>

以下爲正文
---------------------------------------------------------------------
		     Linux 在 AArch64 中的內存布局
		     ===========================

作者: Catalin Marinas <catalin.marinas@arm.com>

本文檔描述 AArch64 Linux 內核所使用的虛擬內存布局。此構架可以實現
頁大小爲 4KB 的 4 級轉換表和頁大小爲 64KB 的 3 級轉換表。

AArch64 Linux 使用 3 級或 4 級轉換表，其頁大小配置爲 4KB，對於用戶和內核
分別都有 39-bit (512GB) 或 48-bit (256TB) 的虛擬地址空間。
對於頁大小爲 64KB的配置，僅使用 2 級轉換表，有 42-bit (4TB) 的虛擬地址空間，但內存布局相同。

用戶地址空間的 63:48 位爲 0，而內核地址空間的相應位爲 1。TTBRx 的
選擇由虛擬地址的 63 位給出。swapper_pg_dir 僅包含內核（全局）映射，
而用戶 pgd 僅包含用戶（非全局）映射。swapper_pg_dir 地址被寫入
TTBR1 中，且從不寫入 TTBR0。

AArch64 Linux 在頁大小爲 4KB，並使用 3 級轉換表時的內存布局：

起始地址			結束地址			大小		用途
-----------------------------------------------------------------------
0000000000000000	0000007fffffffff	 512GB		用戶空間
ffffff8000000000	ffffffffffffffff	 512GB		內核空間

AArch64 Linux 在頁大小爲 4KB，並使用 4 級轉換表時的內存布局：

起始地址			結束地址			大小		用途
-----------------------------------------------------------------------
0000000000000000	0000ffffffffffff	 256TB		用戶空間
ffff000000000000	ffffffffffffffff	 256TB		內核空間

AArch64 Linux 在頁大小爲 64KB，並使用 2 級轉換表時的內存布局：

起始地址			結束地址			大小		用途
-----------------------------------------------------------------------
0000000000000000	000003ffffffffff	   4TB		用戶空間
fffffc0000000000	ffffffffffffffff	   4TB		內核空間

AArch64 Linux 在頁大小爲 64KB，並使用 3 級轉換表時的內存布局：

起始地址			結束地址			大小		用途
-----------------------------------------------------------------------
0000000000000000	0000ffffffffffff	 256TB		用戶空間
ffff000000000000	ffffffffffffffff	 256TB		內核空間

更詳細的內核虛擬內存布局，請參閱內核啓動信息。

4KB 頁大小的轉換表查找：

+--------+--------+--------+--------+--------+--------+--------+--------+
|63    56|55    48|47    40|39    32|31    24|23    16|15     8|7      0|
+--------+--------+--------+--------+--------+--------+--------+--------+
 |                 |         |         |         |         |
 |                 |         |         |         |         v
 |                 |         |         |         |   [11:0]  頁內偏移
 |                 |         |         |         +-> [20:12] L3 索引
 |                 |         |         +-----------> [29:21] L2 索引
 |                 |         +---------------------> [38:30] L1 索引
 |                 +-------------------------------> [47:39] L0 索引
 +-------------------------------------------------> [63] TTBR0/1

64KB 頁大小的轉換表查找：

+--------+--------+--------+--------+--------+--------+--------+--------+
|63    56|55    48|47    40|39    32|31    24|23    16|15     8|7      0|
+--------+--------+--------+--------+--------+--------+--------+--------+
 |                 |    |               |              |
 |                 |    |               |              v
 |                 |    |               |            [15:0]  頁內偏移
 |                 |    |               +----------> [28:16] L3 索引
 |                 |    +--------------------------> [41:29] L2 索引
 |                 +-------------------------------> [47:42] L1 索引
 +-------------------------------------------------> [63] TTBR0/1

當使用 KVM 時, 管理程序（hypervisor）在 EL2 中通過相對內核虛擬地址的
一個固定偏移來映射內核頁（內核虛擬地址的高 24 位設爲零）:

起始地址			結束地址			大小		用途
-----------------------------------------------------------------------
0000004000000000	0000007fffffffff	 256GB		在 HYP 中映射的內核對象


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :ref:`Documentation/arm64/perf.rst <perf_index>`

Translator: Bailu Lin <bailu.lin@vivo.com>
            Hu Haowen <src.res@email.cn>

=============
Perf 事件屬性
=============

:作者: Andrew Murray <andrew.murray@arm.com>
:日期: 2019-03-06

exclude_user
------------

該屬性排除用戶空間。

用戶空間始終運行在 EL0，因此該屬性將排除 EL0。

exclude_kernel
--------------

該屬性排除內核空間。

打開 VHE 時內核運行在 EL2，不打開 VHE 時內核運行在 EL1。客戶機
內核總是運行在 EL1。

對於宿主機，該屬性排除 EL1 和 VHE 上的 EL2。

對於客戶機，該屬性排除 EL1。請注意客戶機從來不會運行在 EL2。

exclude_hv
----------

該屬性排除虛擬機監控器。

對於 VHE 宿主機該屬性將被忽略，此時我們認爲宿主機內核是虛擬機監
控器。

對於 non-VHE 宿主機該屬性將排除 EL2，因爲虛擬機監控器運行在 EL2
的任何代碼主要用於客戶機和宿主機的切換。

對於客戶機該屬性無效。請注意客戶機從來不會運行在 EL2。

exclude_host / exclude_guest
----------------------------

這些屬性分別排除了 KVM 宿主機和客戶機。

KVM 宿主機可能運行在 EL0（用戶空間），EL1（non-VHE 內核）和
EL2（VHE 內核 或 non-VHE 虛擬機監控器）。

KVM 客戶機可能運行在 EL0（用戶空間）和 EL1（內核）。

由於宿主機和客戶機之間重疊的異常級別，我們不能僅僅依靠 PMU 的硬體異
常過濾機制-因此我們必須啓用/禁用對於客戶機進入和退出的計數。而這在
VHE 和 non-VHE 系統上表現不同。

對於 non-VHE 系統的 exclude_host 屬性排除 EL2 - 在進入和退出客戶
機時，我們會根據 exclude_host 和 exclude_guest 屬性在適當的情況下
禁用/啓用該事件。

對於 VHE 系統的 exclude_guest 屬性排除 EL1，而對其中的 exclude_host
屬性同時排除 EL0，EL2。在進入和退出客戶機時，我們會適當地根據
exclude_host 和 exclude_guest 屬性包括/排除 EL0。

以上聲明也適用於在 not-VHE 客戶機使用這些屬性時，但是請注意客戶機從
來不會運行在 EL2。

準確性
------

在 non-VHE 宿主機上，我們在 EL2 進入/退出宿主機/客戶機的切換時啓用/
關閉計數器 -但是在啓用/禁用計數器和進入/退出客戶機之間存在一段延時。
對於 exclude_host， 我們可以通過過濾 EL2 消除在客戶機進入/退出邊界
上用於計數客戶機事件的宿主機事件計數器。但是當使用 !exclude_hv 時，
在客戶機進入/退出有一個小的停電窗口無法捕獲到宿主機的事件。

在 VHE 系統沒有停電窗口。


code:
SPDX-License-Identifier: GPL-2.0

Chinese translated version of Documentation/arm64/silicon-errata.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.

M:	Will Deacon <will.deacon@arm.com>
zh_CN:	Fu Wei <wefu@redhat.com>
zh_TW:	Hu Haowen <src.res@email.cn>
C:	1926e54f115725a9248d0c4c65c22acaf94de4c4
---------------------------------------------------------------------
Documentation/arm64/silicon-errata.rst 的中文翻譯

如果想評論或更新本文的內容，請直接聯繫原文檔的維護者。如果你使用英文
交流有困難的話，也可以向中文版維護者求助。如果本翻譯更新不及時或者翻
譯存在問題，請聯繫中文版維護者。

英文版維護者： Will Deacon <will.deacon@arm.com>
中文版維護者： 傅煒  Fu Wei <wefu@redhat.com>
中文版翻譯者： 傅煒  Fu Wei <wefu@redhat.com>
中文版校譯者： 傅煒  Fu Wei <wefu@redhat.com>
繁體中文版校譯者： 胡皓文  Hu Haowen <src.res@email.cn>
本文翻譯提交時的 Git 檢出點爲： 1926e54f115725a9248d0c4c65c22acaf94de4c4

以下爲正文
---------------------------------------------------------------------
                晶片勘誤和軟體補救措施
                ==================

作者: Will Deacon <will.deacon@arm.com>
日期: 2015年11月27日

一個不幸的現實：硬體經常帶有一些所謂的「瑕疵（errata）」，導致其在
某些特定情況下會違背構架定義的行爲。就基於 ARM 的硬體而言，這些瑕疵
大體可分爲以下幾類：

  A 類：無可行補救措施的嚴重缺陷。
  B 類：有可接受的補救措施的重大或嚴重缺陷。
  C 類：在正常操作中不會顯現的小瑕疵。

更多資訊，請在 infocenter.arm.com （需註冊）中查閱「軟體開發者勘誤
筆記」（「Software Developers Errata Notice」）文檔。

對於 Linux 而言，B 類缺陷可能需要作業系統的某些特別處理。例如，避免
一個特殊的代碼序列，或是以一種特定的方式配置處理器。在某種不太常見的
情況下，爲將 A 類缺陷當作 C 類處理，可能需要用類似的手段。這些手段被
統稱爲「軟體補救措施」，且僅在少數情況需要（例如，那些需要一個運行在
非安全異常級的補救措施 *並且* 能被 Linux 觸發的情況）。

對於尚在討論中的可能對未受瑕疵影響的系統產生干擾的軟體補救措施，有一個
相應的內核配置（Kconfig）選項被加在 「內核特性（Kernel Features）」->
「基於可選方法框架的 ARM 瑕疵補救措施（ARM errata workarounds via
the alternatives framework）"。這些選項被默認開啓，若探測到受影響的CPU，
補丁將在運行時被使用。至於對系統運行影響較小的補救措施，內核配置選項
並不存在，且代碼以某種規避瑕疵的方式被構造（帶注釋爲宜）。

這種做法對於在任意內核原始碼樹中準確地判斷出哪個瑕疵已被軟體方法所補救
稍微有點麻煩，所以在 Linux 內核中此文件作爲軟體補救措施的註冊表，
並將在新的軟體補救措施被提交和向後移植（backported）到穩定內核時被更新。

| 實現者         | 受影響的組件    | 勘誤編號        | 內核配置                |
+----------------+-----------------+-----------------+-------------------------+
| ARM            | Cortex-A53      | #826319         | ARM64_ERRATUM_826319    |
| ARM            | Cortex-A53      | #827319         | ARM64_ERRATUM_827319    |
| ARM            | Cortex-A53      | #824069         | ARM64_ERRATUM_824069    |
| ARM            | Cortex-A53      | #819472         | ARM64_ERRATUM_819472    |
| ARM            | Cortex-A53      | #845719         | ARM64_ERRATUM_845719    |
| ARM            | Cortex-A53      | #843419         | ARM64_ERRATUM_843419    |
| ARM            | Cortex-A57      | #832075         | ARM64_ERRATUM_832075    |
| ARM            | Cortex-A57      | #852523         | N/A                     |
| ARM            | Cortex-A57      | #834220         | ARM64_ERRATUM_834220    |
|                |                 |                 |                         |
| Cavium         | ThunderX ITS    | #22375, #24313  | CAVIUM_ERRATUM_22375    |
| Cavium         | ThunderX GICv3  | #23154          | CAVIUM_ERRATUM_23154    |


code:
SPDX-License-Identifier: GPL-2.0

Chinese translated version of Documentation/arm64/tagged-pointers.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: Will Deacon <will.deacon@arm.com>
Chinese maintainer: Fu Wei <wefu@redhat.com>
Traditional Chinese maintainer: Hu Haowen <src.res@email.cn>
---------------------------------------------------------------------
Documentation/arm64/tagged-pointers.rst 的中文翻譯

如果想評論或更新本文的內容，請直接聯繫原文檔的維護者。如果你使用英文
交流有困難的話，也可以向中文版維護者求助。如果本翻譯更新不及時或者翻
譯存在問題，請聯繫中文版維護者。

英文版維護者： Will Deacon <will.deacon@arm.com>
中文版維護者： 傅煒  Fu Wei <wefu@redhat.com>
中文版翻譯者： 傅煒  Fu Wei <wefu@redhat.com>
中文版校譯者： 傅煒  Fu Wei <wefu@redhat.com>
繁體中文版校譯者： 胡皓文  Hu Haowen <src.res@email.cn>

以下爲正文
---------------------------------------------------------------------
		Linux 在 AArch64 中帶標記的虛擬地址
		=================================

作者: Will Deacon <will.deacon@arm.com>
日期: 2013 年 06 月 12 日

本文檔簡述了在 AArch64 地址轉換系統中提供的帶標記的虛擬地址及其在
AArch64 Linux 中的潛在用途。

內核提供的地址轉換表配置使通過 TTBR0 完成的虛擬地址轉換（即用戶空間
映射），其虛擬地址的最高 8 位（63:56）會被轉換硬體所忽略。這種機制
讓這些位可供應用程式自由使用，其注意事項如下：

	(1) 內核要求所有傳遞到 EL1 的用戶空間地址帶有 0x00 標記。
	    這意味著任何攜帶用戶空間虛擬地址的系統調用（syscall）
	    參數 *必須* 在陷入內核前使它們的最高字節被清零。

	(2) 非零標記在傳遞信號時不被保存。這意味著在應用程式中利用了
	    標記的信號處理函數無法依賴 siginfo_t 的用戶空間虛擬
	    地址所攜帶的包含其內部域信息的標記。此規則的一個例外是
	    當信號是在調試觀察點的異常處理程序中產生的，此時標記的
	    信息將被保存。

	(3) 當使用帶標記的指針時需特別留心，因爲僅對兩個虛擬地址
	    的高字節，C 編譯器很可能無法判斷它們是不同的。

此構架會阻止對帶標記的 PC 指針的利用，因此在異常返回時，其高字節
將被設置成一個爲 「55」 的擴展符。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :doc:`../../../cpu-freq/core`
:Translator: Yanteng Si <siyanteng@loongson.cn>
             Hu Haowen <src.res@email.cn>

.. _tw_core.rst:

====================================
CPUFreq核心和CPUFreq通知器的通用說明
====================================

作者:
	- Dominik Brodowski  <linux@brodo.de>
	- David Kimdon <dwhedon@debian.org>
	- Rafael J. Wysocki <rafael.j.wysocki@intel.com>
	- Viresh Kumar <viresh.kumar@linaro.org>

.. 目錄:

   1.  CPUFreq核心和接口
   2.  CPUFreq通知器
   3.  含有Operating Performance Point (OPP)的CPUFreq表的生成

1. CPUFreq核心和接口
======================

cpufreq核心代碼位於drivers/cpufreq/cpufreq.c中。這些cpufreq代碼爲CPUFreq架構的驅
動程序（那些操作硬體切換頻率的代碼）以及 "通知器 "提供了一個標準化的接口。
這些是設備驅動程序或需要了解策略變化的其它內核部分（如 ACPI 熱量管理）或所有頻率更改（除
計時代碼外），甚至需要強制確定速度限制的通知器（如 ARM 架構上的 LCD 驅動程序）。
此外， 內核 "常數" loops_per_jiffy會根據頻率變化而更新。

cpufreq策略的引用計數由 cpufreq_cpu_get 和 cpufreq_cpu_put 來完成，以確保 cpufreq 驅
動程序被正確地註冊到核心中，並且驅動程序在 cpufreq_put_cpu 被調用之前不會被卸載。這也保證
了每個CPU核的cpufreq 策略在使用期間不會被釋放。

2. CPUFreq 通知器
====================

CPUFreq通知器符合標準的內核通知器接口。
關於通知器的細節請參閱 linux/include/linux/notifier.h。

這裡有兩個不同的CPUfreq通知器 - 策略通知器和轉換通知器。

2.1 CPUFreq策略通知器
----------------------------

當創建或移除策略時，這些都會被通知。

階段是在通知器的第二個參數中指定的。當第一次創建策略時，階段是CPUFREQ_CREATE_POLICY，當
策略被移除時，階段是CPUFREQ_REMOVE_POLICY。

第三個參數 ``void *pointer`` 指向一個結構體cpufreq_policy，其包括min，max(新策略的下限和
上限（單位爲kHz）)這幾個值。

2.2 CPUFreq轉換通知器
--------------------------------

當CPUfreq驅動切換CPU核心頻率時，策略中的每個在線CPU都會收到兩次通知，這些變化沒有任何外部干
預。

第二個參數指定階段 - CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE.

第三個參數是一個包含如下值的結構體cpufreq_freqs：

=====	====================
cpu	受影響cpu的編號
old	舊頻率
new	新頻率
flags	cpufreq驅動的標誌
=====	====================

3. 含有Operating Performance Point (OPP)的CPUFreq表的生成
==================================================================
關於OPP的細節請參閱 Documentation/power/opp.rst

dev_pm_opp_init_cpufreq_table -
	這個功能提供了一個隨時可用的轉換程序，用來將OPP層關於可用頻率的內部信息翻譯成一種容易提供給
	cpufreq的格式。

	.. Warning::

		不要在中斷上下文中使用此函數。

	例如::

	 soc_pm_init()
	 {
		/* Do things */
		r = dev_pm_opp_init_cpufreq_table(dev, &freq_table);
		if (!r)
			policy->freq_table = freq_table;
		/* Do other things */
	 }

	.. note::

		該函數只有在CONFIG_PM_OPP之外還啓用了CONFIG_CPU_FREQ時才可用。

dev_pm_opp_free_cpufreq_table
	釋放dev_pm_opp_init_cpufreq_table分配的表。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :doc:`../../../cpu-freq/cpu-drivers`
:Translator: Yanteng Si <siyanteng@loongson.cn>
             Hu Haowen <src.res@email.cn>

.. _tw_cpu-drivers.rst:

=======================================
如何實現一個新的CPUFreq處理器驅動程序？
=======================================

作者:

	- Dominik Brodowski  <linux@brodo.de>
	- Rafael J. Wysocki <rafael.j.wysocki@intel.com>
	- Viresh Kumar <viresh.kumar@linaro.org>

.. Contents

   1.   怎麼做？
   1.1  初始化
   1.2  Per-CPU 初始化
   1.3  驗證
   1.4  target/target_index 或 setpolicy?
   1.5  target/target_index
   1.6  setpolicy
   1.7  get_intermediate 與 target_intermediate
   2.   頻率表助手

1. 怎麼做？
===========

如此，你剛剛得到了一個全新的CPU/晶片組及其數據手冊，並希望爲這個CPU/晶片組添加cpufreq
支持？很好，這裡有一些至關重要的提示：

1.1 初始化
----------

首先，在__initcall_level_7 (module_init())或更靠後的函數中檢查這個內核是否
運行在正確的CPU和正確的晶片組上。如果是，則使用cpufreq_register_driver()向
CPUfreq核心層註冊一個cpufreq_driver結構體。

結構體cpufreq_driver應該包含什麼成員?

 .name - 驅動的名字。

 .init - 一個指向per-policy初始化函數的指針。

 .verify - 一個指向"verification"函數的指針。

 .setpolicy 或 .fast_switch 或 .target 或 .target_index - 差異見
 下文。

並且可選擇

 .flags - cpufreq核的提示。

 .driver_data - cpufreq驅動程序的特定數據。

 .get_intermediate 和 target_intermediate - 用於在改變CPU頻率時切換到穩定
 的頻率。

 .get - 返回CPU的當前頻率。

 .bios_limit - 返回HW/BIOS對CPU的最大頻率限制值。

 .exit - 一個指向per-policy清理函數的指針，該函數在cpu熱插拔過程的CPU_POST_DEAD
 階段被調用。

 .suspend - 一個指向per-policy暫停函數的指針，該函數在關中斷且在該策略的調節器停止
 後被調用。

 .resume - 一個指向per-policy恢復函數的指針，該函數在關中斷且在調節器再一次開始前被
 調用。

 .ready - 一個指向per-policy準備函數的指針，該函數在策略完全初始化之後被調用。

 .attr - 一個指向NULL結尾的"struct freq_attr"列表的指針，該函數允許導出值到
 sysfs。

 .boost_enabled - 如果設置，則啓用提升(boost)頻率。

 .set_boost - 一個指向per-policy函數的指針，該函數用來開啓/關閉提升(boost)頻率功能。

1.2 Per-CPU 初始化
------------------

每當一個新的CPU被註冊到設備模型中，或者在cpufreq驅動註冊自己之後，如果此CPU的cpufreq策
略不存在，則會調用per-policy的初始化函數cpufreq_driver.init。請注意，.init()和.exit()程序
只對策略調用一次，而不是對策略管理的每個CPU調用一次。它需要一個 ``struct cpufreq_policy
*policy`` 作爲參數。現在該怎麼做呢？

如果有必要，請在你的CPU上激活CPUfreq功能支持。

然後，驅動程序必須填寫以下數值:

+-----------------------------------+--------------------------------------+
|policy->cpuinfo.min_freq 和	   |					  |
|policy->cpuinfo.max_freq	    | 該CPU支持的最低和最高頻率（kHz）     |
|				    |                                      |
|				    | 				           |
+-----------------------------------+--------------------------------------+
|policy->cpuinfo.transition_latency |                                      |
|				    | CPU在兩個頻率之間切換所需的時間，以  |
|				    | 納秒爲單位（如適用，否則指定         |
|				    | CPUFREQ_ETERNAL）                    |
+-----------------------------------+--------------------------------------+
|policy->cur			    | 該CPU當前的工作頻率(如適用)          |
|				    |                                      |
+-----------------------------------+--------------------------------------+
|policy->min,			    |					   |
|policy->max,			    |					   |
|policy->policy and, if necessary,  |					   |
|policy->governor		    | 必須包含該cpu的 「默認策略」。稍後   |
|				    | 會用這些值調用                       |
|				    | cpufreq_driver.verify and either     |
|				    | cpufreq_driver.setpolicy or          |
|				    | cpufreq_driver.target/target_index   |
|				    | 		                           |
+-----------------------------------+--------------------------------------+
|policy->cpus			    | 用與這個CPU一起做DVFS的(在線+離線)   |
|				    | CPU(即與它共享時鐘/電壓軌)的掩碼更新 |
|				    | 這個                                 |
|				    |                                      |
+-----------------------------------+--------------------------------------+

對於設置其中的一些值(cpuinfo.min[max]_freq, policy->min[max])，頻率表助手可能會有幫
助。關於它們的更多信息，請參見第2節。

1.3 驗證
--------

當用戶決定設置一個新的策略(由 「policy,governor,min,max組成」)時，必須對這個策略進行驗證，
以便糾正不兼容的值。爲了驗證這些值，cpufreq_verify_within_limits(``struct cpufreq_policy
*policy``, ``unsigned int min_freq``, ``unsigned int max_freq``)函數可能會有幫助。
關於頻率表助手的詳細內容請參見第2節。

您需要確保至少有一個有效頻率（或工作範圍）在 policy->min 和 policy->max 範圍內。如果有必
要，先增加policy->max，只有在沒有辦法的情況下，才減少policy->min。

1.4 target 或 target_index 或 setpolicy 或 fast_switch?
-------------------------------------------------------

大多數cpufreq驅動甚至大多數cpu頻率升降算法只允許將CPU頻率設置爲預定義的固定值。對於這些，你
可以使用->target()，->target_index()或->fast_switch()回調。

有些cpufreq功能的處理器可以自己在某些限制之間切換頻率。這些應使用->setpolicy()回調。

1.5. target/target_index
------------------------

target_index調用有兩個參數：``struct cpufreq_policy * policy``和``unsigned int``
索引(於列出的頻率表)。

當調用這裡時，CPUfreq驅動必須設置新的頻率。實際頻率必須由freq_table[index].frequency決定。

它應該總是在錯誤的情況下恢復到之前的頻率(即policy->restore_freq)，即使我們之前切換到中間頻率。

已棄用
----------
目標調用有三個參數。``struct cpufreq_policy * policy``, unsigned int target_frequency,
unsigned int relation.

CPUfreq驅動在調用這裡時必須設置新的頻率。實際的頻率必須使用以下規則來確定。

- 緊跟 "目標頻率"。
- policy->min <= new_freq <= policy->max (這必須是有效的!!!)
- 如果 relation==CPUFREQ_REL_L，嘗試選擇一個高於或等於 target_freq 的 new_freq。("L代表
  最低，但不能低於")
- 如果 relation==CPUFREQ_REL_H，嘗試選擇一個低於或等於 target_freq 的 new_freq。("H代表
  最高，但不能高於")

這裡，頻率表助手可能會幫助你--詳見第2節。

1.6. fast_switch
----------------

這個函數用於從調度器的上下文進行頻率切換。並非所有的驅動都要實現它，因爲不允許在這個回調中睡眠。這
個回調必須經過高度優化，以儘可能快地進行切換。

這個函數有兩個參數： ``struct cpufreq_policy *policy`` 和 ``unsigned int target_frequency``。

1.7 setpolicy
-------------

setpolicy調用只需要一個``struct cpufreq_policy * policy``作爲參數。需要將處理器內或晶片組內動態頻
率切換的下限設置爲policy->min，上限設置爲policy->max，如果支持的話，當policy->policy爲
CPUFREQ_POLICY_PERFORMANCE時選擇面向性能的設置，當CPUFREQ_POLICY_POWERSAVE時選擇面向省電的設置。
也可以查看drivers/cpufreq/longrun.c中的參考實現。

1.8 get_intermediate 和 target_intermediate
--------------------------------------------

僅適用於 target_index() 和 CPUFREQ_ASYNC_NOTIFICATION 未設置的驅動。

get_intermediate應該返回一個平台想要切換到的穩定的中間頻率，target_intermediate()應該將CPU設置爲
該頻率，然後再跳轉到'index'對應的頻率。核心會負責發送通知，驅動不必在target_intermediate()或
target_index()中處理。

在驅動程序不想因爲某個目標頻率切換到中間頻率的情況下，它們可以從get_intermediate()中返回'0'。在這種情況
下，核心將直接調用->target_index()。

注意：->target_index()應該在失敗的情況下恢復到policy->restore_freq，因爲core會爲此發送通知。

2. 頻率表助手
=============

由於大多數cpufreq處理器只允許被設置爲幾個特定的頻率，因此，一個帶有一些函數的 「頻率表」可能會輔助處理器驅動
程序的一些工作。這樣的 "頻率表" 由一個cpufreq_frequency_table條目構成的數組組成，"driver_data" 中包
含了驅動程序的具體數值，"frequency" 中包含了相應的頻率，並設置了標誌。在表的最後，需要添加一個
cpufreq_frequency_table條目，頻率設置爲CPUFREQ_TABLE_END。而如果想跳過表中的一個條目，則將頻率設置爲
CPUFREQ_ENTRY_INVALID。這些條目不需要按照任何特定的順序排序，但如果它們是cpufreq 核心會對它們進行快速的DVFS，
因爲搜索最佳匹配會更快。

如果策略在其policy->freq_table欄位中包含一個有效的指針，cpufreq表就會被核心自動驗證。

cpufreq_frequency_table_verify()保證至少有一個有效的頻率在policy->min和policy->max範圍內，並且所有其他
標準都被滿足。這對->verify調用很有幫助。

cpufreq_frequency_table_target()是對應於->target階段的頻率表助手。只要把數值傳遞給這個函數，這個函數就會返
回包含CPU要設置的頻率的頻率表條目。

以下宏可以作爲cpufreq_frequency_table的疊代器。

cpufreq_for_each_entry(pos, table) - 遍歷頻率表的所有條目。

cpufreq_for_each_valid_entry(pos, table) - 該函數遍歷所有條目，不包括CPUFREQ_ENTRY_INVALID頻率。
使用參數 "pos"-一個``cpufreq_frequency_table * `` 作爲循環變量，使用參數 "table"-作爲你想疊代
的``cpufreq_frequency_table * `` 。

例如::

	struct cpufreq_frequency_table *pos, *driver_freq_table;

	cpufreq_for_each_entry(pos, driver_freq_table) {
		/* Do something with pos */
		pos->frequency = ...
	}

如果你需要在driver_freq_table中處理pos的位置，不要減去指針，因爲它的代價相當高。相反，使用宏
cpufreq_for_each_entry_idx() 和 cpufreq_for_each_valid_entry_idx() 。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :doc:`../../../cpu-freq/cpufreq-stats`
:Translator: Yanteng Si <siyanteng@loongson.cn>
             Hu Haowen <src.res@email.cn>

.. _tw_cpufreq-stats.rst:

==========================================
sysfs CPUFreq Stats的一般說明
==========================================

用戶信息

作者: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>

.. Contents

   1. 簡介
   2. 提供的統計數據(舉例說明)
   3. 配置cpufreq-stats

1. 簡介
===============

cpufreq-stats是一個爲每個CPU提供CPU頻率統計的驅動。
這些統計數據在/sysfs中以一堆只讀接口的形式提供。這個接口（在配置好後）將出現在
/sysfs（<sysfs root>/devices/system/cpu/cpuX/cpufreq/stats/）中cpufreq下的一個單
獨的目錄中，提供給每個CPU。
各種統計數據將在此目錄下形成只讀文件。

此驅動是獨立於任何可能運行在你所用CPU上的特定cpufreq_driver而設計的。因此，它將與所有
cpufreq_driver一起工作。

2. 提供的統計數據(舉例說明)
=====================================

cpufreq stats提供了以下統計數據（在下面詳細解釋）。

-  time_in_state
-  total_trans
-  trans_table

所有的統計數據將從統計驅動被載入的時間（或統計被重置的時間）開始，到某一統計數據被讀取的時間爲止。
顯然，統計驅動不會有任何關於統計驅動載入之前的頻率轉換信息。

::

    <mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # ls -l
    total 0
    drwxr-xr-x  2 root root    0 May 14 16:06 .
    drwxr-xr-x  3 root root    0 May 14 15:58 ..
    --w-------  1 root root 4096 May 14 16:06 reset
    -r--r--r--  1 root root 4096 May 14 16:06 time_in_state
    -r--r--r--  1 root root 4096 May 14 16:06 total_trans
    -r--r--r--  1 root root 4096 May 14 16:06 trans_table

- **reset**

只寫屬性，可用於重置統計計數器。這對於評估不同調節器下的系統行爲非常有用，且無需重啓。

- **time_in_state**

此項給出了這個CPU所支持的每個頻率所花費的時間。cat輸出的每一行都會有"<frequency>
<time>"對，表示這個CPU在<frequency>上花費了<time>個usertime單位的時間。這裡的
usertime單位是10mS（類似於/proc中輸出的其他時間）。

::

    <mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # cat time_in_state
    3600000 2089
    3400000 136
    3200000 34
    3000000 67
    2800000 172488

- **total_trans**

給出了這個CPU上頻率轉換的總次數。cat的輸出將有一個單一的計數，這就是頻率轉換的總數。

::

    <mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # cat total_trans
    20

- **trans_table**

這將提供所有CPU頻率轉換的細粒度信息。這裡的cat輸出是一個二維矩陣，其中一個條目<i, j>（第
i行，第j列）代表從Freq_i到Freq_j的轉換次數。Freq_i行和Freq_j列遵循驅動最初提供給cpufreq
核的頻率表的排序順序，因此可以排序（升序或降序）或不排序。 這裡的輸出也包含了每行每列的實際
頻率值，以便更好地閱讀。

如果轉換表大於PAGE_SIZE，讀取時將返回一個-EFBIG錯誤。

::

    <mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # cat trans_table
    From  :    To
	    :   3600000   3400000   3200000   3000000   2800000
    3600000:         0         5         0         0         0
    3400000:         4         0         2         0         0
    3200000:         0         1         0         2         0
    3000000:         0         0         1         0         3
    2800000:         0         0         0         2         0

3. 配置cpufreq-stats
============================

要在你的內核中配置cpufreq-stats::

	Config Main Menu
		Power management options (ACPI, APM)  --->
			CPU Frequency scaling  --->
				[*] CPU Frequency scaling
				[*]   CPU frequency translation statistics

"CPU Frequency scaling" (CONFIG_CPU_FREQ) 應該被啓用以配置cpufreq-stats。

"CPU frequency translation statistics" (CONFIG_CPU_FREQ_STAT)提供了包括
time_in_state、total_trans和trans_table的統計數據。

一旦啓用了這個選項，並且你的CPU支持cpufrequency，你就可以在/sysfs中看到CPU頻率統計。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :doc:`../../../cpu-freq/index`
:Translator: Yanteng Si <siyanteng@loongson.cn>
             Hu Haowen <src.res@email.cn>

.. _tw_index.rst:

=======================================================
Linux CPUFreq - Linux(TM)內核中的CPU頻率和電壓升降代碼
=======================================================

Author: Dominik Brodowski  <linux@brodo.de>

      時鐘升降允許你在運行中改變CPU的時鐘速度。這是一個很好的節省電池電量的方法，因爲時
      鐘速度越低，CPU消耗的電量越少。

.. toctree::
   :maxdepth: 1

   core
   cpu-drivers
   cpufreq-stats

郵件列表
------------
這裡有一個 CPU 頻率變化的 CVS 提交和通用列表，您可以在這裡報告bug、問題或提交補丁。要發
布消息，請發送電子郵件到 linux-pm@vger.kernel.org。

連結
-----
FTP檔案:
* ftp://ftp.linux.org.uk/pub/linux/cpufreq/

如何訪問CVS倉庫:
* http://cvs.arm.linux.org.uk/

CPUFreq郵件列表:
* http://vger.kernel.org/vger-lists.html#linux-pm

SA-1100的時鐘和電壓標度:
* http://www.lartmaker.nl/projects/scaling


code:
:orphan:

.. warning::
     此文件的目的是爲讓中文讀者更容易閱讀和理解，而不是作爲一個分支。因此，
     如果您對此文件有任何意見或改動，請先嘗試更新原始英文文件。如果要更改或
     修正某處翻譯文件，請將意見或補丁發送給維護者（聯繫方式見下）。

.. note::
     如果您發現本文檔與原始文件有任何不同或者有翻譯問題，請聯繫該文件的譯者，
     或者發送電子郵件給胡皓文以獲取幫助：<src.res@email.cn>。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :doc:`../../../filesystems/debugfs`

=======
Debugfs
=======

譯者
::

	中文版維護者：羅楚成 Chucheng Luo <luochucheng@vivo.com>
	中文版翻譯者：羅楚成 Chucheng Luo <luochucheng@vivo.com>
	中文版校譯者: 羅楚成 Chucheng Luo <luochucheng@vivo.com>
	繁體中文版校譯者: 胡皓文 Hu Haowen <src.res@email.cn>

版權所有2020 羅楚成 <luochucheng@vivo.com>
版權所有2021 胡皓文 Hu Haowen <src.res@email.cn>

Debugfs是內核開發人員在用戶空間獲取信息的簡單方法。與/proc不同，proc只提供進程
信息。也不像sysfs,具有嚴格的「每個文件一個值「的規則。debugfs根本沒有規則,開發
人員可以在這裡放置他們想要的任何信息。debugfs文件系統也不能用作穩定的ABI接口。
從理論上講，debugfs導出文件的時候沒有任何約束。但是[1]實際情況並不總是那麼
簡單。即使是debugfs接口，也最好根據需要進行設計,並儘量保持接口不變。

Debugfs通常使用以下命令安裝::

    mount -t debugfs none /sys/kernel/debug

（或等效的/etc/fstab行）。
debugfs根目錄默認僅可由root用戶訪問。要更改對文件樹的訪問，請使用「 uid」，「 gid」
和「 mode」掛載選項。請注意，debugfs API僅按照GPL協議導出到模塊。

使用debugfs的代碼應包含<linux/debugfs.h>。然後，首先是創建至少一個目錄來保存
一組debugfs文件::

    struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);

如果成功，此調用將在指定的父目錄下創建一個名爲name的目錄。如果parent參數爲空，
則會在debugfs根目錄中創建。創建目錄成功時，返回值是一個指向dentry結構體的指針。
該dentry結構體的指針可用於在目錄中創建文件（以及最後將其清理乾淨）。ERR_PTR
（-ERROR）返回值表明出錯。如果返回ERR_PTR（-ENODEV），則表明內核是在沒有debugfs
支持的情況下構建的，並且下述函數都不會起作用。

在debugfs目錄中創建文件的最通用方法是::

    struct dentry *debugfs_create_file(const char *name, umode_t mode,
				       struct dentry *parent, void *data,
				       const struct file_operations *fops);

在這裡，name是要創建的文件的名稱，mode描述了訪問文件應具有的權限，parent指向
應該保存文件的目錄，data將存儲在產生的inode結構體的i_private欄位中，而fops是
一組文件操作函數，這些函數中實現文件操作的具體行爲。至少，read（）和/或
write（）操作應提供；其他可以根據需要包括在內。同樣的，返回值將是指向創建文件
的dentry指針，錯誤時返回ERR_PTR（-ERROR），系統不支持debugfs時返回值爲ERR_PTR
（-ENODEV）。創建一個初始大小的文件，可以使用以下函數代替::

    struct dentry *debugfs_create_file_size(const char *name, umode_t mode,
				struct dentry *parent, void *data,
				const struct file_operations *fops,
				loff_t file_size);

file_size是初始文件大小。其他參數跟函數debugfs_create_file的相同。

在許多情況下，沒必要自己去創建一組文件操作;對於一些簡單的情況,debugfs代碼提供
了許多幫助函數。包含單個整數值的文件可以使用以下任何一項創建::

    void debugfs_create_u8(const char *name, umode_t mode,
			   struct dentry *parent, u8 *value);
    void debugfs_create_u16(const char *name, umode_t mode,
			    struct dentry *parent, u16 *value);
    struct dentry *debugfs_create_u32(const char *name, umode_t mode,
				      struct dentry *parent, u32 *value);
    void debugfs_create_u64(const char *name, umode_t mode,
			    struct dentry *parent, u64 *value);

這些文件支持讀取和寫入給定值。如果某個文件不支持寫入，只需根據需要設置mode
參數位。這些文件中的值以十進位表示；如果需要使用十六進位，可以使用以下函數
替代::

    void debugfs_create_x8(const char *name, umode_t mode,
			   struct dentry *parent, u8 *value);
    void debugfs_create_x16(const char *name, umode_t mode,
			    struct dentry *parent, u16 *value);
    void debugfs_create_x32(const char *name, umode_t mode,
			    struct dentry *parent, u32 *value);
    void debugfs_create_x64(const char *name, umode_t mode,
			    struct dentry *parent, u64 *value);

這些功能只有在開發人員知道導出值的大小的時候才有用。某些數據類型在不同的架構上
有不同的寬度，這樣會使情況變得有些複雜。在這種特殊情況下可以使用以下函數::

    void debugfs_create_size_t(const char *name, umode_t mode,
			       struct dentry *parent, size_t *value);

不出所料，此函數將創建一個debugfs文件來表示類型爲size_t的變量。

同樣地，也有導出無符號長整型變量的函數，分別以十進位和十六進位表示如下::

    struct dentry *debugfs_create_ulong(const char *name, umode_t mode,
					struct dentry *parent,
					unsigned long *value);
    void debugfs_create_xul(const char *name, umode_t mode,
			    struct dentry *parent, unsigned long *value);

布爾值可以通過以下方式放置在debugfs中::

    struct dentry *debugfs_create_bool(const char *name, umode_t mode,
				       struct dentry *parent, bool *value);

讀取結果文件將產生Y（對於非零值）或N，後跟換行符寫入的時候，它只接受大寫或小寫
值或1或0。任何其他輸入將被忽略。

同樣，atomic_t類型的值也可以放置在debugfs中::

    void debugfs_create_atomic_t(const char *name, umode_t mode,
				 struct dentry *parent, atomic_t *value)

讀取此文件將獲得atomic_t值，寫入此文件將設置atomic_t值。

另一個選擇是通過以下結構體和函數導出一個任意二進位數據塊::

    struct debugfs_blob_wrapper {
	void *data;
	unsigned long size;
    };

    struct dentry *debugfs_create_blob(const char *name, umode_t mode,
				       struct dentry *parent,
				       struct debugfs_blob_wrapper *blob);

讀取此文件將返回由指針指向debugfs_blob_wrapper結構體的數據。一些驅動使用「blobs」
作爲一種返回幾行（靜態）格式化文本的簡單方法。這個函數可用於導出二進位信息，但
似乎在主線中沒有任何代碼這樣做。請注意，使用debugfs_create_blob（）命令創建的
所有文件是只讀的。

如果您要轉儲一個寄存器塊（在開發過程中經常會這麼做，但是這樣的調試代碼很少上傳
到主線中。Debugfs提供兩個函數：一個用於創建僅寄存器文件，另一個把一個寄存器塊
插入一個順序文件中::

    struct debugfs_reg32 {
	char *name;
	unsigned long offset;
    };

    struct debugfs_regset32 {
	struct debugfs_reg32 *regs;
	int nregs;
	void __iomem *base;
    };

    struct dentry *debugfs_create_regset32(const char *name, umode_t mode,
				     struct dentry *parent,
				     struct debugfs_regset32 *regset);

    void debugfs_print_regs32(struct seq_file *s, struct debugfs_reg32 *regs,
			 int nregs, void __iomem *base, char *prefix);

「base」參數可能爲0，但您可能需要使用__stringify構建reg32數組，實際上有許多寄存器
名稱（宏）是寄存器塊在基址上的字節偏移量。

如果要在debugfs中轉儲u32數組，可以使用以下函數創建文件::

     void debugfs_create_u32_array(const char *name, umode_t mode,
			struct dentry *parent,
			u32 *array, u32 elements);

「array」參數提供數據，而「elements」參數爲數組中元素的數量。注意：數組創建後，數組
大小無法更改。

有一個函數來創建與設備相關的seq_file::

   struct dentry *debugfs_create_devm_seqfile(struct device *dev,
				const char *name,
				struct dentry *parent,
				int (*read_fn)(struct seq_file *s,
					void *data));

「dev」參數是與此debugfs文件相關的設備，並且「read_fn」是一個函數指針，這個函數在
列印seq_file內容的時候被回調。

還有一些其他的面向目錄的函數::

    struct dentry *debugfs_rename(struct dentry *old_dir,
		                  struct dentry *old_dentry,
		                  struct dentry *new_dir,
				  const char *new_name);

    struct dentry *debugfs_create_symlink(const char *name,
                                          struct dentry *parent,
                                          const char *target);

調用debugfs_rename()將爲現有的debugfs文件重命名，可能同時切換目錄。 new_name
函數調用之前不能存在；返回值爲old_dentry，其中包含更新的信息。可以使用
debugfs_create_symlink（）創建符號連結。

所有debugfs用戶必須考慮的一件事是：

debugfs不會自動清除在其中創建的任何目錄。如果一個模塊在不顯式刪除debugfs目錄的
情況下卸載模塊，結果將會遺留很多野指針，從而導致系統不穩定。因此，所有debugfs
用戶-至少是那些可以作爲模塊構建的用戶-必須做模塊卸載的時候準備刪除在此創建的
所有文件和目錄。一份文件可以通過以下方式刪除::

    void debugfs_remove(struct dentry *dentry);

dentry值可以爲NULL或錯誤值，在這種情況下，不會有任何文件被刪除。

很久以前，內核開發者使用debugfs時需要記錄他們創建的每個dentry指針，以便最後所有
文件都可以被清理掉。但是，現在debugfs用戶能調用以下函數遞歸清除之前創建的文件::

    void debugfs_remove_recursive(struct dentry *dentry);

如果將對應頂層目錄的dentry傳遞給以上函數，則該目錄下的整個層次結構將會被刪除。

注釋：
[1] http://lwn.net/Articles/309298/


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :ref:`Documentation/filesystems/index.rst <filesystems_index>`
:Translator: Wang Wenhu <wenhu.wang@vivo.com>
             Hu Haowen <src.res@email.cn>

.. _tw_filesystems_index:

========================
Linux Kernel中的文件系統
========================

這份正在開發的手冊或許在未來某個輝煌的日子裡以易懂的形式將Linux虛擬\
文件系統（VFS）層以及基於其上的各種文件系統如何工作呈現給大家。當前\
可以看到下面的內容。

文件系統
========

文件系統實現文檔。

.. toctree::
   :maxdepth: 2

   virtiofs
   debugfs
   tmpfs


code:
SPDX-License-Identifier: GPL-2.0

Chinese translated version of Documentation/filesystems/sysfs.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: Patrick Mochel	<mochel@osdl.org>
		Mike Murphy <mamurph@cs.clemson.edu>
Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
---------------------------------------------------------------------
Documentation/filesystems/sysfs.rst 的中文翻譯

如果想評論或更新本文的內容，請直接聯繫原文檔的維護者。如果你使用英文
交流有困難的話，也可以向中文版維護者求助。如果本翻譯更新不及時或者翻
譯存在問題，請聯繫中文版維護者。
英文版維護者： Patrick Mochel	<mochel@osdl.org>
		Mike Murphy <mamurph@cs.clemson.edu>
中文版維護者： 傅煒 Fu Wei <tekkamanninja@gmail.com>
中文版翻譯者： 傅煒 Fu Wei <tekkamanninja@gmail.com>
中文版校譯者： 傅煒 Fu Wei <tekkamanninja@gmail.com>
繁體中文版校譯者：胡皓文 Hu Haowen <src.res@email.cn>

以下爲正文
---------------------------------------------------------------------
sysfs - 用於導出內核對象(kobject)的文件系統

Patrick Mochel	<mochel@osdl.org>
Mike Murphy <mamurph@cs.clemson.edu>

修訂:    16 August 2011
原始版本:   10 January 2003

sysfs 簡介:
~~~~~~~~~~

sysfs 是一個最初基於 ramfs 且位於內存的文件系統。它提供導出內核
數據結構及其屬性，以及它們之間的關聯到用戶空間的方法。

sysfs 始終與 kobject 的底層結構緊密相關。請閱讀
Documentation/core-api/kobject.rst 文檔以獲得更多關於 kobject 接口的
信息。

使用 sysfs
~~~~~~~~~~~

只要內核配置中定義了 CONFIG_SYSFS ，sysfs 總是被編譯進內核。你可
通過以下命令掛載它:

    mount -t sysfs sysfs /sys

創建目錄
~~~~~~~~

任何 kobject 在系統中註冊，就會有一個目錄在 sysfs 中被創建。這個
目錄是作爲該 kobject 的父對象所在目錄的子目錄創建的，以準確地傳遞
內核的對象層次到用戶空間。sysfs 中的頂層目錄代表著內核對象層次的
共同祖先；例如：某些對象屬於某個子系統。

Sysfs 在與其目錄關聯的 kernfs_node 對象中內部保存一個指向實現
目錄的 kobject 的指針。以前，這個 kobject 指針被 sysfs 直接用於
kobject 文件打開和關閉的引用計數。而現在的 sysfs 實現中，kobject
引用計數只能通過 sysfs_schedule_callback() 函數直接修改。

屬性
~~~~

kobject 的屬性可在文件系統中以普通文件的形式導出。Sysfs 爲屬性定義
了面向文件 I/O 操作的方法，以提供對內核屬性的讀寫。

屬性應爲 ASCII 碼文本文件。以一個文件只存儲一個屬性值爲宜。但一個
文件只包含一個屬性值可能影響效率，所以一個包含相同數據類型的屬性值
數組也被廣泛地接受。

混合類型、表達多行數據以及一些怪異的數據格式會遭到強烈反對。這樣做是
很丟臉的,而且其代碼會在未通知作者的情況下被重寫。

一個簡單的屬性結構定義如下:

struct attribute {
        char                    * name;
        struct module		*owner;
        umode_t                 mode;
};

int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);

一個單獨的屬性結構並不包含讀寫其屬性值的方法。子系統最好爲增刪特定
對象類型的屬性定義自己的屬性結構體和封裝函數。

例如:驅動程序模型定義的 device_attribute 結構體如下:

struct device_attribute {
	struct attribute	attr;
	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
			char *buf);
	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count);
};

int device_create_file(struct device *, const struct device_attribute *);
void device_remove_file(struct device *, const struct device_attribute *);

爲了定義設備屬性，同時定義了一下輔助宏:

#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

例如:聲明

static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo);

等同於如下代碼：

static struct device_attribute dev_attr_foo = {
       .attr	= {
		.name = "foo",
		.mode = S_IWUSR | S_IRUGO,
		.show = show_foo,
		.store = store_foo,
	},
};

子系統特有的回調函數
~~~~~~~~~~~~~~~~~~~

當一個子系統定義一個新的屬性類型時，必須實現一系列的 sysfs 操作，
以幫助讀寫調用實現屬性所有者的顯示和儲存方法。

struct sysfs_ops {
        ssize_t (*show)(struct kobject *, struct attribute *, char *);
        ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};

[子系統應已經定義了一個 struct kobj_type 結構體作爲這個類型的
描述符，並在此保存 sysfs_ops 的指針。更多的信息參見 kobject 的
文檔]

sysfs 會爲這個類型調用適當的方法。當一個文件被讀寫時，這個方法會
將一般的kobject 和 attribute 結構體指針轉換爲適當的指針類型後
調用相關聯的函數。

示例:

#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)

static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
                             char *buf)
{
        struct device_attribute *dev_attr = to_dev_attr(attr);
        struct device *dev = kobj_to_dev(kobj);
        ssize_t ret = -EIO;

        if (dev_attr->show)
                ret = dev_attr->show(dev, dev_attr, buf);
        if (ret >= (ssize_t)PAGE_SIZE) {
                printk("dev_attr_show: %pS returned bad count\n",
                                dev_attr->show);
        }
        return ret;
}

讀寫屬性數據
~~~~~~~~~~~~

在聲明屬性時，必須指定 show() 或 store() 方法，以實現屬性的
讀或寫。這些方法的類型應該和以下的設備屬性定義一樣簡單。

ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
                 const char *buf, size_t count);

也就是說,他們應只以一個處理對象、一個屬性和一個緩衝指針作爲參數。

sysfs 會分配一個大小爲 (PAGE_SIZE) 的緩衝區並傳遞給這個方法。
Sysfs 將會爲每次讀寫操作調用一次這個方法。這使得這些方法在執行時
會出現以下的行爲:

- 在讀方面（read(2)），show() 方法應該填充整個緩衝區。回想屬性
  應只導出了一個屬性值或是一個同類型屬性值的數組，所以這個代價將
  不會不太高。

  這使得用戶空間可以局部地讀和任意的向前搜索整個文件。如果用戶空間
  向後搜索到零或使用『0』偏移執行一個pread(2)操作，show()方法將
  再次被調用，以重新填充緩存。

- 在寫方面（write(2)），sysfs 希望在第一次寫操作時得到整個緩衝區。
  之後 Sysfs 傳遞整個緩衝區給 store() 方法。

  當要寫 sysfs 文件時，用戶空間進程應首先讀取整個文件，修該想要
  改變的值，然後回寫整個緩衝區。

  在讀寫屬性值時，屬性方法的執行應操作相同的緩衝區。

註記:

- 寫操作導致的 show() 方法重載，會忽略當前文件位置。

- 緩衝區應總是 PAGE_SIZE 大小。對於i386，這個值爲4096。

- show() 方法應該返回寫入緩衝區的字節數，也就是 scnprintf()的
  返回值。

- show() 方法在將格式化返回值返回用戶空間的時候，禁止使用snprintf()。
  如果可以保證不會發生緩衝區溢出，可以使用sprintf()，否則必須使用
  scnprintf()。

- store() 應返回緩衝區的已用字節數。如果整個緩存都已填滿，只需返回
  count 參數。

- show() 或 store() 可以返回錯誤值。當得到一個非法值，必須返回一個
  錯誤值。

- 一個傳遞給方法的對象將會通過 sysfs 調用對象內嵌的引用計數固定在
  內存中。儘管如此，對象代表的物理實體(如設備)可能已不存在。如有必要，
  應該實現一個檢測機制。

一個簡單的(未經實驗證實的)設備屬性實現如下：

static ssize_t show_name(struct device *dev, struct device_attribute *attr,
                         char *buf)
{
	return scnprintf(buf, PAGE_SIZE, "%s\n", dev->name);
}

static ssize_t store_name(struct device *dev, struct device_attribute *attr,
                          const char *buf, size_t count)
{
        snprintf(dev->name, sizeof(dev->name), "%.*s",
                 (int)min(count, sizeof(dev->name) - 1), buf);
	return count;
}

static DEVICE_ATTR(name, S_IRUGO, show_name, store_name);

（注意：真正的實現不允許用戶空間設置設備名。）

頂層目錄布局
~~~~~~~~~~~~

sysfs 目錄的安排顯示了內核數據結構之間的關係。

頂層 sysfs 目錄如下:

block/
bus/
class/
dev/
devices/
firmware/
net/
fs/

devices/ 包含了一個設備樹的文件系統表示。他直接映射了內部的內核
設備樹，反映了設備的層次結構。

bus/ 包含了內核中各種總線類型的平面目錄布局。每個總線目錄包含兩個
子目錄:

	devices/
	drivers/

devices/ 包含了系統中出現的每個設備的符號連結，他們指向 root/ 下的
設備目錄。

drivers/ 包含了每個已爲特定總線上的設備而掛載的驅動程序的目錄(這裡
假定驅動沒有跨越多個總線類型)。

fs/ 包含了一個爲文件系統設立的目錄。現在每個想要導出屬性的文件系統必須
在 fs/ 下創建自己的層次結構(參見Documentation/filesystems/fuse.rst)。

dev/ 包含兩個子目錄： char/ 和 block/。在這兩個子目錄中，有以
<major>:<minor> 格式命名的符號連結。這些符號連結指向 sysfs 目錄
中相應的設備。/sys/dev 提供一個通過一個 stat(2) 操作結果，查找
設備 sysfs 接口快捷的方法。

更多有關 driver-model 的特性信息可以在 Documentation/driver-api/driver-model/
中找到。

TODO: 完成這一節。

當前接口
~~~~~~~~

以下的接口層普遍存在於當前的sysfs中:

- 設備 (include/linux/device.h)
----------------------------------
結構體:

struct device_attribute {
	struct attribute	attr;
	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
			char *buf);
	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count);
};

聲明:

DEVICE_ATTR(_name, _mode, _show, _store);

增/刪屬性:

int device_create_file(struct device *dev, const struct device_attribute * attr);
void device_remove_file(struct device *dev, const struct device_attribute * attr);

- 總線驅動程序 (include/linux/device.h)
--------------------------------------
結構體:

struct bus_attribute {
        struct attribute        attr;
        ssize_t (*show)(struct bus_type *, char * buf);
        ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
};

聲明:

BUS_ATTR(_name, _mode, _show, _store)

增/刪屬性:

int bus_create_file(struct bus_type *, struct bus_attribute *);
void bus_remove_file(struct bus_type *, struct bus_attribute *);

- 設備驅動程序 (include/linux/device.h)
-----------------------------------------

結構體:

struct driver_attribute {
        struct attribute        attr;
        ssize_t (*show)(struct device_driver *, char * buf);
        ssize_t (*store)(struct device_driver *, const char * buf,
                         size_t count);
};

聲明:

DRIVER_ATTR(_name, _mode, _show, _store)

增/刪屬性：

int driver_create_file(struct device_driver *, const struct driver_attribute *);
void driver_remove_file(struct device_driver *, const struct driver_attribute *);

文檔
~~~~

sysfs 目錄結構以及其中包含的屬性定義了一個內核與用戶空間之間的 ABI。
對於任何 ABI，其自身的穩定和適當的文檔是非常重要的。所有新的 sysfs
屬性必須在 Documentation/ABI 中有文檔。詳見 Documentation/ABI/README。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: Documentation/filesystems/tmpfs.rst

Translated by Wang Qing <wangqing@vivo.com>
and Hu Haowen <src.res@email.cn>

=====
Tmpfs
=====

Tmpfs是一個將所有文件都保存在虛擬內存中的文件系統。

tmpfs中的所有內容都是臨時的，也就是說沒有任何文件會在硬碟上創建。
如果卸載tmpfs實例，所有保存在其中的文件都會丟失。

tmpfs將所有文件保存在內核緩存中，隨著文件內容增長或縮小可以將不需要的
頁面swap出去。它具有最大限制，可以通過「mount -o remount ...」調整。

和ramfs（創建tmpfs的模板）相比，tmpfs包含交換和限制檢查。和tmpfs相似的另
一個東西是RAM磁碟（/dev/ram*），可以在物理RAM中模擬固定大小的硬碟，並在
此之上創建一個普通的文件系統。Ramdisks無法swap，因此無法調整它們的大小。

由於tmpfs完全保存於頁面緩存和swap中，因此所有tmpfs頁面將在/proc/meminfo
中顯示爲「Shmem」，而在free(1)中顯示爲「Shared」。請注意，這些計數還包括
共享內存(shmem，請參閱ipcs(1))。獲得計數的最可靠方法是使用df(1)和du(1)。

tmpfs具有以下用途：

1) 內核總有一個無法看到的內部掛載，用於共享匿名映射和SYSV共享內存。

   掛載不依賴於CONFIG_TMPFS。如果CONFIG_TMPFS未設置，tmpfs對用戶不可見。
   但是內部機制始終存在。

2) glibc 2.2及更高版本期望將tmpfs掛載在/dev/shm上以用於POSIX共享內存
   (shm_open，shm_unlink)。添加內容到/etc/fstab應注意如下：

	tmpfs	/dev/shm	tmpfs	defaults	0 0

   使用時需要記住創建掛載tmpfs的目錄。

   SYSV共享內存無需掛載，內部已默認支持。(在2.3內核版本中，必須掛載
   tmpfs的前身(shm fs)才能使用SYSV共享內存)

3) 很多人（包括我）都覺的在/tmp和/var/tmp上掛載非常方便，並具有較大的
   swap分區。目前循環掛載tmpfs可以正常工作，所以大多數發布都應當可以
   使用mkinitrd通過/tmp訪問/tmp。

4) 也許還有更多我不知道的地方:-)

tmpfs有三個用於調整大小的掛載選項：

=========  ===========================================================
size       tmpfs實例分配的字節數限制。默認值是不swap時物理RAM的一半。
           如果tmpfs實例過大，機器將死鎖，因爲OOM處理將無法釋放該內存。
nr_blocks  與size相同，但以PAGE_SIZE爲單位。
nr_inodes  tmpfs實例的最大inode個數。默認值是物理內存頁數的一半，或者
           (有高端內存的機器)低端內存RAM的頁數，二者以較低者為準。
=========  ===========================================================

這些參數接受後綴k，m或g表示千，兆和千兆字節，可以在remount時更改。
size參數也接受後綴％用來限制tmpfs實例占用物理RAM的百分比：
未指定size或nr_blocks時，默認值爲size=50％

如果nr_blocks=0（或size=0），block個數將不受限制；如果nr_inodes=0，
inode個數將不受限制。這樣掛載通常是不明智的，因爲它允許任何具有寫權限的
用戶通過訪問tmpfs耗盡機器上的所有內存；但同時這樣做也會增強在多個CPU的
場景下的訪問。

tmpfs具有爲所有文件設置NUMA內存分配策略掛載選項(如果啓用了CONFIG_NUMA),
可以通過「mount -o remount ...」調整

======================== =========================
mpol=default             採用進程分配策略
                         (請參閱 set_mempolicy(2))
mpol=prefer:Node         傾向從給定的節點分配
mpol=bind:NodeList       只允許從指定的鍊表分配
mpol=interleave          傾向於依次從每個節點分配
mpol=interleave:NodeList 依次從每個節點分配
mpol=local               優先本地節點分配內存
======================== =========================

NodeList格式是以逗號分隔的十進位數字表示大小和範圍，最大和最小範圍是用-
分隔符的十進位數來表示。例如，mpol=bind0-3,5,7,9-15

帶有有效NodeList的內存策略將按指定格式保存，在創建文件時使用。當任務在該
文件系統上創建文件時，會使用到掛載時的內存策略NodeList選項，如果設置的話，
由調用任務的cpuset[請參見Documentation/admin-guide/cgroup-v1/cpusets.rst]
以及下面列出的可選標誌約束。如果NodeLists爲設置爲空集，則文件的內存策略將
恢復爲「默認」策略。

NUMA內存分配策略有可選標誌，可以用於模式結合。在掛載tmpfs時指定這些可選
標誌可以在NodeList之前生效。
Documentation/admin-guide/mm/numa_memory_policy.rst列出所有可用的內存
分配策略模式標誌及其對內存策略。

::

	=static		相當於	MPOL_F_STATIC_NODES
	=relative	相當於	MPOL_F_RELATIVE_NODES

例如，mpol=bind=staticNodeList相當於MPOL_BIND|MPOL_F_STATIC_NODES的分配策略

請注意，如果內核不支持NUMA，那麼使用mpol選項掛載tmpfs將會失敗；nodelist指定不
在線的節點也會失敗。如果您的系統依賴於此，但內核會運行不帶NUMA功能(也許是安全
revocery內核)，或者具有較少的節點在線，建議從自動模式中省略mpol選項掛載選項。
可以在以後通過「mount -o remount,mpol=Policy:NodeList MountPoint」添加到掛載點。

要指定初始根目錄，可以使用如下掛載選項：

====	====================
模式	權限用八進位數字表示
uid	用戶ID
gid	組ID
====	====================

這些選項對remount沒有任何影響。您可以通過chmod(1),chown(1)和chgrp(1)的更改
已經掛載的參數。

tmpfs具有選擇32位還是64位inode的掛載選項：

=======   =============
inode64   使用64位inode
inode32   使用32位inode
=======   =============

在32位內核上，默認是inode32，掛載時指定inode64會被拒絕。
在64位內核上，默認配置是CONFIG_TMPFS_INODE64。inode64避免了單個設備上可能有多個
具有相同inode編號的文件；比如32位應用程式使用glibc如果長期訪問tmpfs，一旦達到33
位inode編號，就有EOVERFLOW失敗的危險，無法打開大於2GiB的文件，並返回EINVAL。

所以'mount -t tmpfs -o size=10G,nr_inodes=10k,mode=700 tmpfs /mytmpfs'將在
/mytmpfs上掛載tmpfs實例，分配只能由root用戶訪問的10GB RAM/SWAP，可以有10240個
inode的實例。

:作者:
   Christoph Rohland <cr@sap.com>, 1.12.01
:更新:
   Hugh Dickins, 4 June 2007
:更新:
   KOSAKI Motohiro, 16 Mar 2010
:更新:
   Chris Down, 13 July 2020


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :ref:`Documentation/filesystems/virtiofs.rst <virtiofs_index>`

譯者
::

	中文版維護者： 王文虎 Wang Wenhu <wenhu.wang@vivo.com>
	中文版翻譯者： 王文虎 Wang Wenhu <wenhu.wang@vivo.com>
	中文版校譯者： 王文虎 Wang Wenhu <wenhu.wang@vivo.com>
	中文版校譯者： 王文虎 Wang Wenhu <wenhu.wang@vivo.com>
	繁體中文版校譯者：胡皓文 Hu Haowen <src.res@email.cn>

===========================================
virtiofs: virtio-fs 主機<->客機共享文件系統
===========================================

- Copyright (C) 2020 Vivo Communication Technology Co. Ltd.

介紹
====
Linux的virtiofs文件系統實現了一個半虛擬化VIRTIO類型「virtio-fs」設備的驅動，通過該\
類型設備實現客機<->主機文件系統共享。它允許客機掛載一個已經導出到主機的目錄。

客機通常需要訪問主機或者遠程系統上的文件。使用場景包括：在新客機安裝時讓文件對其\
可見；從主機上的根文件系統啓動；對無狀態或臨時客機提供持久存儲和在客機之間共享目錄。

儘管在某些任務可能通過使用已有的網絡文件系統完成，但是卻需要非常難以自動化的配置\
步驟，且將存儲網絡暴露給客機。而virtio-fs設備通過提供不經過網絡的文件系統訪問文件\
的設計方式解決了這些問題。

另外，virto-fs設備發揮了主客機共存的優點提高了性能，並且提供了網絡文件系統所不具備
的一些語義功能。

用法
====
以``myfs``標籤將文件系統掛載到``/mnt``:

.. code-block:: sh

  guest# mount -t virtiofs myfs /mnt

請查閱 https://virtio-fs.gitlab.io/ 了解配置QEMU和virtiofsd守護程序的詳細信息。

內幕
====
由於virtio-fs設備將FUSE協議用於文件系統請求，因此Linux的virtiofs文件系統與FUSE文\
件系統客戶端緊密集成在一起。客機充當FUSE客戶端而主機充當FUSE伺服器，內核與用戶空\
間之間的/dev/fuse接口由virtio-fs設備接口代替。

FUSE請求被置於虛擬隊列中由主機處理。主機填充緩衝區中的響應部分，而客機處理請求的完成部分。

將/dev/fuse映射到虛擬隊列需要解決/dev/fuse和虛擬隊列之間語義上的差異。每次讀取\
/dev/fuse設備時，FUSE客戶端都可以選擇要傳輸的請求，從而可以使某些請求優先於其他\
請求。虛擬隊列有其隊列語義，無法更改已入隊請求的順序。在虛擬隊列已滿的情況下尤
其關鍵，因爲此時不可能加入高優先級的請求。爲了解決此差異，virtio-fs設備採用「hiprio」\
（高優先級）虛擬隊列，專門用於有別於普通請求的高優先級請求。


code:
Chinese translated version of Documentation/admin-guide/gpio

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: Grant Likely <grant.likely@secretlab.ca>
		Linus Walleij <linus.walleij@linaro.org>
Traditional Chinese maintainer: Hu Haowen <src.res@email.cn>
---------------------------------------------------------------------
Documentation/admin-guide/gpio 的繁體中文翻譯

如果想評論或更新本文的內容，請直接聯繫原文檔的維護者。如果你使用英文
交流有困難的話，也可以向繁體中文版維護者求助。如果本翻譯更新不及時或
者翻譯存在問題，請聯繫繁體中文版維護者。

英文版維護者： Grant Likely <grant.likely@secretlab.ca>
		Linus Walleij <linus.walleij@linaro.org>
繁體中文版維護者： 胡皓文 Hu Haowen <src.res@email.cn>
繁體中文版翻譯者： 胡皓文 Hu Haowen <src.res@email.cn>
繁體中文版校譯者： 胡皓文 Hu Haowen <src.res@email.cn>

以下爲正文
---------------------------------------------------------------------
GPIO 接口

本文檔提供了一個在Linux下訪問GPIO的公約概述。

這些函數以 gpio_* 作爲前綴。其他的函數不允許使用這樣的前綴或相關的
__gpio_* 前綴。

什麼是GPIO?
==========
"通用輸入/輸出口"(GPIO)是一個靈活的由軟體控制的數位訊號。他們可
由多種晶片提供,且對於從事嵌入式和定製硬體的 Linux 開發者來說是
比較熟悉。每個GPIO 都代表一個連接到特定引腳或球柵陣列(BGA)封裝中
「球珠」的一個位。電路板原理圖顯示了 GPIO 與外部硬體的連接關係。
驅動可以編寫成通用代碼，以使板級啓動代碼可傳遞引腳配置數據給驅動。

片上系統 (SOC) 處理器對 GPIO 有很大的依賴。在某些情況下,每個
非專用引腳都可配置爲 GPIO,且大多數晶片都最少有一些 GPIO。
可編程邏輯器件(類似 FPGA) 可以方便地提供 GPIO。像電源管理和
音頻編解碼器這樣的多功能晶片經常留有一些這樣的引腳來幫助那些引腳
匱乏的 SOC。同時還有通過 I2C 或 SPI 串行總線連接的「GPIO擴展器」
晶片。大多數 PC 的南橋有一些擁有 GPIO 能力的引腳 (只有BIOS
固件才知道如何使用他們)。

GPIO 的實際功能因系統而異。通常用法有:

  - 輸出值可寫 (高電平=1，低電平=0)。一些晶片也有如何驅動這些值的選項,
    例如只允許輸出一個值、支持「線與」及其他取值類似的模式(值得注意的是
    「開漏」信號)

  - 輸入值可讀(1、0)。一些晶片支持引腳在配置爲「輸出」時回讀，這對於類似
    「線與」的情況(以支持雙向信號)是非常有用的。GPIO 控制器可能有輸入
    去毛刺/消抖邏輯,這有時需要軟體控制。

  - 輸入通常可作爲 IRQ 信號,一般是沿觸發,但有時是電平觸發。這樣的 IRQ
    可能配置爲系統喚醒事件,以將系統從低功耗狀態下喚醒。

  - 通常一個 GPIO 根據不同產品電路板的需求,可以配置爲輸入或輸出,也有僅
    支持單向的。

  - 大部分 GPIO 可以在持有自旋鎖時訪問,但是通常由串行總線擴展的 GPIO
    不允許持有自旋鎖。但某些系統也支持這種類型。

對於給定的電路板,每個 GPIO 都用於某個特定的目的,如監控 MMC/SD 卡的
插入/移除、檢測卡的防寫狀態、驅動 LED、配置收發器、模擬串行總線、
復位硬體看門狗、感知開關狀態等等。

GPIO 公約
=========
注意,這個叫做「公約」，因爲這不是強制性的，不遵循這個公約是無傷大雅的，
因爲此時可移植性並不重要。GPIO 常用於板級特定的電路邏輯,甚至可能
隨著電路板的版本而改變，且不可能在不同走線的電路板上使用。僅有在少數
功能上才具有可移植性，其他功能是平台特定。這也是由於「膠合」的邏輯造成的。

此外，這不需要任何的執行框架，只是一個接口。某個平台可能通過一個簡單地
訪問晶片寄存器的內聯函數來實現它，其他平台可能通過委託一系列不同的GPIO
控制器的抽象函數來實現它。(有一些可選的代碼能支持這種策略的實現,本文檔
後面會介紹，但作爲 GPIO 接口的客戶端驅動程序必須與它的實現無關。)

也就是說,如果在他們的平台上支持這個公約，驅動應儘可能的使用它。同時，平台
必須在 Kconfig 中選擇 ARCH_REQUIRE_GPIOLIB 或者 ARCH_WANT_OPTIONAL_GPIOLIB
選項。那些調用標準 GPIO 函數的驅動應該在 Kconfig 入口中聲明依賴GENERIC_GPIO。
當驅動包含文件:

	#include <linux/gpio.h>

則 GPIO 函數是可用,無論是「真實代碼」還是經優化過的語句。如果你遵守
這個公約，當你的代碼完成後，對其他的開發者來說會更容易看懂和維護。

注意，這些操作包含所用平台的 I/O 屏障代碼，驅動無須顯式地調用他們。

標識 GPIO
---------
GPIO 是通過無符號整型來標識的,範圍是 0 到 MAX_INT。保留「負」數
用於其他目的,例如標識信號「在這個板子上不可用」或指示錯誤。未接觸底層
硬體的代碼會忽略這些整數。

平台會定義這些整數的用法,且通常使用 #define 來定義 GPIO，這樣
板級特定的啓動代碼可以直接關聯相應的原理圖。相對來說，驅動應該僅使用
啓動代碼傳遞過來的 GPIO 編號，使用 platform_data 保存板級特定
引腳配置數據 (同時還有其他須要的板級特定數據)，避免可能出現的問題。

例如一個平台使用編號 32-159 來標識 GPIO,而在另一個平台使用編號0-63
標識一組 GPIO 控制器,64-79標識另一類 GPIO 控制器,且在一個含有
FPGA 的特定板子上使用 80-95。編號不一定要連續,那些平台中，也可以
使用編號2000-2063來標識一個 I2C 接口的 GPIO 擴展器中的 GPIO。

如果你要初始化一個帶有無效 GPIO 編號的結構體,可以使用一些負編碼
(如"-EINVAL")，那將使其永遠不會是有效。來測試這樣一個結構體中的編號
是否關聯一個 GPIO，你可使用以下斷言:

	int gpio_is_valid(int number);

如果編號不存在，則請求和釋放 GPIO 的函數將拒絕執行相關操作(見下文)。
其他編號也可能被拒絕,比如一個編號可能存在，但暫時在給定的電路上不可用。

一個平台是否支持多個 GPIO 控制器爲平台特定的實現問題，就像是否可以
在 GPIO 編號空間中有「空洞」和是否可以在運行時添加新的控制器一樣。
這些問題會影響其他事情，包括相鄰的 GPIO 編號是否存在等。

使用 GPIO
---------
對於一個 GPIO，系統應該做的第一件事情就是通過 gpio_request()
函數分配它，見下文。

接下來是設置I/O方向，這通常是在板級啓動代碼中爲所使用的 GPIO 設置
platform_device 時完成。

	/* 設置爲輸入或輸出, 返回 0 或負的錯誤代碼 */
	int gpio_direction_input(unsigned gpio);
	int gpio_direction_output(unsigned gpio, int value);

返回值爲零代表成功，否則返回一個負的錯誤代碼。這個返回值需要檢查，因爲
get/set(獲取/設置)函數調用沒法返回錯誤,且有可能是配置錯誤。通常，
你應該在進程上下文中調用這些函數。然而,對於自旋鎖安全的 GPIO，在板子
啓動的早期、進程啓動前使用他們也是可以的。

對於作爲輸出的 GPIO，爲其提供初始輸出值，對於避免在系統啓動期間出現
信號毛刺是很有幫助的。

爲了與傳統的 GPIO 接口兼容, 在設置一個 GPIO 方向時，如果它還未被申請，
則隱含了申請那個 GPIO 的操作(見下文)。這種兼容性正在從可選的 gpiolib
框架中移除。

如果這個 GPIO 編碼不存在，或者特定的 GPIO 不能用於那種模式，則方向
設置可能失敗。依賴啓動固件來正確地設置方向通常是一個壞主意，因爲它可能
除了啓動Linux，並沒有做更多的驗證工作。(同理, 板子的啓動代碼可能需要
將這個復用的引腳設置爲 GPIO，並正確地配置上拉/下拉電阻。)

訪問自旋鎖安全的 GPIO
-------------------
大多數 GPIO 控制器可以通過內存讀/寫指令來訪問。這些指令不會休眠,可以
安全地在硬(非線程)中斷例程和類似的上下文中完成。

對於那些用 gpio_cansleep()測試總是返回失敗的 GPIO(見下文)，使用
以下的函數訪問:

	/* GPIO 輸入:返回零或非零 */
	int gpio_get_value(unsigned gpio);

	/* GPIO 輸出 */
	void gpio_set_value(unsigned gpio, int value);

GPIO值是布爾值，零表示低電平，非零表示高電平。當讀取一個輸出引腳的值時，
返回值應該是引腳上的值。這個值不總是和輸出值相符，因爲存在開漏輸出信號和
輸出延遲問題。

以上的 get/set 函數無錯誤返回值，因爲之前 gpio_direction_*()應已檢查過
其是否爲「無效GPIO」。此外，還需要注意的是並不是所有平台都可以從輸出引腳
中讀取數據，對於不能讀取的引腳應總返回零。另外，對那些在原子上下文中無法
安全訪問的 GPIO (譯者註：因爲訪問可能導致休眠)使用這些函數是不合適的
(見下文)。

在 GPIO 編號(還有輸出、值)爲常數的情況下,鼓勵通過平台特定的實現來優化
這兩個函數來訪問 GPIO 值。這種情況(讀寫一個硬體寄存器)下只需要幾條指令
是很正常的,且無須自旋鎖。這種優化函數比起那些在子程序上花費許多指令的
函數可以使得模擬接口(譯者注:例如 GPIO 模擬 I2C、1-wire 或 SPI)的
應用(在空間和時間上都)更具效率。

訪問可能休眠的 GPIO
-----------------
某些 GPIO 控制器必須通過基於總線(如 I2C 或 SPI)的消息訪問。讀或寫這些
GPIO 值的命令需要等待其信息排到隊首才發送命令，再獲得其反饋。期間需要
休眠，這不能在 IRQ 例程(中斷上下文)中執行。

支持此類 GPIO 的平台通過以下函數返回非零值來區分出這種 GPIO。(此函數需要
一個之前通過 gpio_request 分配到的有效 GPIO 編號):

	int gpio_cansleep(unsigned gpio);

爲了訪問這種 GPIO,內核定義了一套不同的函數:

	/* GPIO 輸入:返回零或非零 ,可能會休眠 */
	int gpio_get_value_cansleep(unsigned gpio);

	/* GPIO 輸出,可能會休眠 */
	void gpio_set_value_cansleep(unsigned gpio, int value);

訪問這樣的 GPIO 需要一個允許休眠的上下文，例如線程 IRQ 處理例程，並用以上的
訪問函數替換那些沒有 cansleep()後綴的自旋鎖安全訪問函數。

除了這些訪問函數可能休眠，且它們操作的 GPIO 不能在硬體 IRQ 處理例程中訪問的
事實，這些處理例程實際上和自旋鎖安全的函數是一樣的。

** 除此之外 ** 調用設置和配置此類 GPIO 的函數也必須在允許休眠的上下文中，
因爲它們可能也需要訪問 GPIO 控制器晶片: (這些設置函數通常在板級啓動代碼或者
驅動探測/斷開代碼中，所以這是一個容易滿足的約束條件。)

	gpio_direction_input()
	gpio_direction_output()
	gpio_request()

## 	gpio_request_one()
##	gpio_request_array()
## 	gpio_free_array()

	gpio_free()
	gpio_set_debounce()

聲明和釋放 GPIO
----------------------------
爲了有助於捕獲系統配置錯誤,定義了兩個函數。

	/* 申請 GPIO, 返回 0 或負的錯誤代碼.
	 * 非空標籤可能有助於診斷.
	 */
	int gpio_request(unsigned gpio, const char *label);

	/* 釋放之前聲明的 GPIO */
	void gpio_free(unsigned gpio);

將無效的 GPIO 編碼傳遞給 gpio_request()會導致失敗，申請一個已使用這個
函數聲明過的 GPIO 也會失敗。gpio_request()的返回值必須檢查。你應該在
進程上下文中調用這些函數。然而,對於自旋鎖安全的 GPIO,在板子啓動的早期、
進入進程之前是可以申請的。

這個函數完成兩個基本的目標。一是標識那些實際上已作爲 GPIO 使用的信號線，
這樣便於更好地診斷;系統可能需要服務幾百個可用的 GPIO，但是對於任何一個
給定的電路板通常只有一些被使用。另一個目的是捕獲衝突，查明錯誤:如兩個或
更多驅動錯誤地認爲他們已經獨占了某個信號線,或是錯誤地認爲移除一個管理著
某個已激活信號的驅動是安全的。也就是說，申請 GPIO 的作用類似一種鎖機制。

某些平台可能也使用 GPIO 作爲電源管理激活信號(例如通過關閉未使用晶片區和
簡單地關閉未使用時鐘)。

對於 GPIO 使用 pinctrl 子系統已知的引腳，子系統應該被告知其使用情況；
一個 gpiolib 驅動的 .request()操作應調用 pinctrl_gpio_request()，
而 gpiolib 驅動的 .free()操作應調用 pinctrl_gpio_free()。pinctrl
子系統允許 pinctrl_gpio_request()在某個引腳或引腳組以復用形式「屬於」
一個設備時都成功返回。

任何須將 GPIO 信號導向適當引腳的引腳復用硬體的編程應該發生在 GPIO
驅動的 .direction_input()或 .direction_output()函數中，以及
任何輸出 GPIO 值的設置之後。這樣可使從引腳特殊功能到 GPIO 的轉換
不會在引腳產生毛刺波形。有時當用一個 GPIO 實現其信號驅動一個非 GPIO
硬體模塊的解決方案時，就需要這種機制。

某些平台允許部分或所有 GPIO 信號使用不同的引腳。類似的，GPIO 或引腳的
其他方面也需要配置，如上拉/下拉。平台軟體應該在對這些 GPIO 調用
gpio_request()前將這類細節配置好，例如使用 pinctrl 子系統的映射表，
使得 GPIO 的用戶無須關注這些細節。

還有一個值得注意的是在釋放 GPIO 前，你必須停止使用它。

注意:申請一個 GPIO 並沒有以任何方式配置它，只不過標識那個 GPIO 處於使用
狀態。必須有另外的代碼來處理引腳配置(如控制 GPIO 使用的引腳、上拉/下拉)。
考慮到大多數情況下聲明 GPIO 之後就會立即配置它們,所以定義了以下三個輔助函數:

	/* 申請一個 GPIO 信號, 同時通過特定的'flags'初始化配置,
	 * 其他和 gpio_request()的參數和返回值相同
	 *
	 */
	int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);

	/* 在單個函數中申請多個 GPIO
	 */
	int gpio_request_array(struct gpio *array, size_t num);

	/* 在單個函數中釋放多個 GPIO
	 */
	void gpio_free_array(struct gpio *array, size_t num);

這裡 'flags' 當前定義可指定以下屬性:

	* GPIOF_DIR_IN		- 配置方向爲輸入
	* GPIOF_DIR_OUT		- 配置方向爲輸出

	* GPIOF_INIT_LOW	- 在作爲輸出時,初始值爲低電平
	* GPIOF_INIT_HIGH	- 在作爲輸出時,初始值爲高電平
	* GPIOF_OPEN_DRAIN	- gpio引腳爲開漏信號
	* GPIOF_OPEN_SOURCE	- gpio引腳爲源極開路信號

	* GPIOF_EXPORT_DIR_FIXED	- 將 gpio 導出到 sysfs，並保持方向
	* GPIOF_EXPORT_DIR_CHANGEABLE	- 同樣是導出, 但允許改變方向

因爲 GPIOF_INIT_* 僅有在配置爲輸出的時候才存在,所以有效的組合爲:

	* GPIOF_IN		- 配置爲輸入
	* GPIOF_OUT_INIT_LOW	- 配置爲輸出,並初始化爲低電平
	* GPIOF_OUT_INIT_HIGH	- 配置爲輸出,並初始化爲高電平

當設置 flag 爲 GPIOF_OPEN_DRAIN 時，則假設引腳是開漏信號。這樣的引腳
將不會在輸出模式下置1。這樣的引腳需要連接上拉電阻。通過使能這個標誌，gpio庫
將會在被要求輸出模式下置1時將引腳變爲輸入狀態來使引腳置高。引腳在輸出模式下
通過置0使其輸出低電平。

當設置 flag 爲 GPIOF_OPEN_SOURCE 時，則假設引腳爲源極開路信號。這樣的引腳
將不會在輸出模式下置0。這樣的引腳需要連接下拉電阻。通過使能這個標誌，gpio庫
將會在被要求輸出模式下置0時將引腳變爲輸入狀態來使引腳置低。引腳在輸出模式下
通過置1使其輸出高電平。

將來這些標誌可能擴展到支持更多的屬性。

更進一步,爲了更簡單地聲明/釋放多個 GPIO,'struct gpio'被引進來封裝所有
這三個領域:

	struct gpio {
		unsigned	gpio;
		unsigned long	flags;
		const char	*label;
	};

一個典型的用例:

	static struct gpio leds_gpios[] = {
		{ 32, GPIOF_OUT_INIT_HIGH, "Power LED" }, /* 默認開啓 */
		{ 33, GPIOF_OUT_INIT_LOW,  "Green LED" }, /* 默認關閉 */
		{ 34, GPIOF_OUT_INIT_LOW,  "Red LED"   }, /* 默認關閉 */
		{ 35, GPIOF_OUT_INIT_LOW,  "Blue LED"  }, /* 默認關閉 */
		{ ... },
	};

	err = gpio_request_one(31, GPIOF_IN, "Reset Button");
	if (err)
		...

	err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios));
	if (err)
		...

	gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios));

GPIO 映射到 IRQ
--------------------
GPIO 編號是無符號整數;IRQ 編號也是。這些構成了兩個邏輯上不同的命名空間
(GPIO 0 不一定使用 IRQ 0)。你可以通過以下函數在它們之間實現映射:

	/* 映射 GPIO 編號到 IRQ 編號 */
	int gpio_to_irq(unsigned gpio);

	/* 映射 IRQ 編號到 GPIO 編號 (儘量避免使用) */
	int irq_to_gpio(unsigned irq);

它們的返回值爲對應命名空間的相關編號，或是負的錯誤代碼(如果無法映射)。
(例如,某些 GPIO 無法做爲 IRQ 使用。)以下的編號錯誤是未經檢測的:使用一個
未通過 gpio_direction_input()配置爲輸入的 GPIO 編號，或者使用一個
並非來源於gpio_to_irq()的 IRQ 編號。

這兩個映射函數可能會在信號編號的加減計算過程上花些時間。它們不可休眠。

gpio_to_irq()返回的非錯誤值可以傳遞給 request_irq()或者 free_irq()。
它們通常通過板級特定的初始化代碼存放到平台設備的 IRQ 資源中。注意:IRQ
觸發選項是 IRQ 接口的一部分，如 IRQF_TRIGGER_FALLING，系統喚醒能力
也是如此。

irq_to_gpio()返回的非錯誤值大多數通常可以被 gpio_get_value()所使用，
比如在 IRQ 是沿觸發時初始化或更新驅動狀態。注意某些平台不支持反映射,所以
你應該儘量避免使用它。

模擬開漏信號
----------------------------
有時在只有低電平信號作爲實際驅動結果(譯者注:多個輸出連接於一點，邏輯電平
結果爲所有輸出的邏輯與)的時候,共享的信號線需要使用「開漏」信號。(該術語
適用於 CMOS 管；而 TTL 用「集電極開路」。)一個上拉電阻使信號爲高電平。這
有時被稱爲「線與」。實際上，從負邏輯(低電平爲真)的角度來看，這是一個「線或」。

一個開漏信號的常見例子是共享的低電平使能 IRQ 信號線。此外,有時雙向數據總線
信號也使用漏極開路信號。

某些 GPIO 控制器直接支持開漏輸出，還有許多不支持。當你需要開漏信號，但
硬體又不直接支持的時候，一個常用的方法是用任何即可作輸入也可作輸出的 GPIO
引腳來模擬:

 LOW:	gpio_direction_output(gpio, 0) ... 這代碼驅動信號並覆蓋
	上拉配置。

 HIGH:	gpio_direction_input(gpio) ... 這代碼關閉輸出,所以上拉電阻
	(或其他的一些器件)控制了信號。

如果你將信號線「驅動」爲高電平，但是 gpio_get_value(gpio)報告了一個
低電平(在適當的上升時間後)，你就可以知道是其他的一些組件將共享信號線拉低了。
這不一定是錯誤的。一個常見的例子就是 I2C 時鐘的延長：一個需要較慢時鐘的
從設備延遲 SCK 的上升沿，而 I2C 主設備相應地調整其信號傳輸速率。

這些公約忽略了什麼?
================
這些公約忽略的最大一件事就是引腳復用，因爲這屬於高度晶片特定的屬性且
沒有可移植性。某個平台可能不需要明確的復用信息；有的對於任意給定的引腳
可能只有兩個功能選項；有的可能每個引腳有八個功能選項；有的可能可以將
幾個引腳中的任何一個作爲給定的 GPIO。(是的，這些例子都來自於當前運行
Linux 的系統。)

在某些系統中,與引腳復用相關的是配置和使能集成的上、下拉模式。並不是所有
平台都支持這種模式,或者不會以相同的方式來支持這種模式；且任何給定的電路板
可能使用外置的上拉(或下拉)電阻,這時晶片上的就不應該使用。(當一個電路需要
5kOhm 的拉動電阻,晶片上的 100 kOhm 電阻就不能做到。)同樣的，驅動能力
(2 mA vs 20 mA)和電壓(1.8V vs 3.3V)是平台特定問題,就像模型一樣在
可配置引腳和 GPIO 之間(沒)有一一對應的關係。

還有其他一些系統特定的機制沒有在這裡指出，例如上述的輸入去毛刺和線與輸出
選項。硬體可能支持批量讀或寫 GPIO，但是那一般是配置相關的：對於處於同一
塊區(bank)的GPIO。(GPIO 通常以 16 或 32 個組成一個區塊，一個給定的
片上系統一般有幾個這樣的區塊。)某些系統可以通過輸出 GPIO 觸發 IRQ，
或者從並非以 GPIO 管理的引腳取值。這些機制的相關代碼沒有必要具有可移植性。

當前，動態定義 GPIO 並不是標準的，例如作爲配置一個帶有某些 GPIO 擴展器的
附加電路板的副作用。

GPIO 實現者的框架 (可選)
=====================
前面提到了，有一個可選的實現框架，讓平台使用相同的編程接口，更加簡單地支持
不同種類的 GPIO 控制器。這個框架稱爲"gpiolib"。

作爲一個輔助調試功能，如果 debugfs 可用，就會有一個 /sys/kernel/debug/gpio
文件。通過這個框架，它可以列出所有註冊的控制器,以及當前正在使用中的 GPIO
的狀態。

控制器驅動: gpio_chip
-------------------
在框架中每個 GPIO 控制器都包裝爲一個 "struct gpio_chip"，他包含了
該類型的每個控制器的常用信息:

 - 設置 GPIO 方向的方法
 - 用於訪問 GPIO 值的方法
 - 告知調用其方法是否可能休眠的標誌
 - 可選的 debugfs 信息導出方法 (顯示類似上拉配置一樣的額外狀態)
 - 診斷標籤

也包含了來自 device.platform_data 的每個實例的數據：它第一個 GPIO 的
編號和它可用的 GPIO 的數量。

實現 gpio_chip 的代碼應支持多控制器實例，這可能使用驅動模型。那些代碼要
配置每個 gpio_chip，並發起gpiochip_add()。卸載一個 GPIO 控制器很少見，
但在必要的時候可以使用 gpiochip_remove()。

大部分 gpio_chip 是一個實例特定結構體的一部分，而並不將 GPIO 接口單獨
暴露出來,比如編址、電源管理等。類似編解碼器這樣的晶片會有複雜的非 GPIO
狀態。

任何一個 debugfs 信息導出方法通常應該忽略還未申請作爲 GPIO 的信號線。
他們可以使用 gpiochip_is_requested()測試，當這個 GPIO 已經申請過了
就返回相關的標籤，否則返回 NULL。

平台支持
-------
爲了支持這個框架，一個平台的 Kconfig 文件將會 "select"(選擇)
ARCH_REQUIRE_GPIOLIB 或 ARCH_WANT_OPTIONAL_GPIOLIB，並讓它的
<asm/gpio.h> 包含 <asm-generic/gpio.h>，同時定義三個方法:
gpio_get_value()、gpio_set_value()和 gpio_cansleep()。

它也應提供一個 ARCH_NR_GPIOS 的定義值，這樣可以更好地反映該平台 GPIO
的實際數量,節省靜態表的空間。(這個定義值應該包含片上系統內建 GPIO 和
GPIO 擴展器中的數據。)

ARCH_REQUIRE_GPIOLIB 意味著 gpiolib 核心在這個構架中將總是編譯進內核。

ARCH_WANT_OPTIONAL_GPIOLIB 意味著 gpiolib 核心默認關閉,且用戶可以
使能它,並將其編譯進內核(可選)。

如果這些選項都沒被選擇,該平台就不通過 GPIO-lib 支持 GPIO,且代碼不可以
被用戶使能。

以下這些方法的實現可以直接使用框架代碼,並總是通過 gpio_chip 調度:

  #define gpio_get_value	__gpio_get_value
  #define gpio_set_value	__gpio_set_value
  #define gpio_cansleep		__gpio_cansleep

這些定義可以用更理想的實現方法替代，那就是使用經過邏輯優化的內聯函數來訪問
基於特定片上系統的 GPIO。例如,若引用的 GPIO (寄存器位偏移)是常量「12」，
讀取或設置它可能只需少則兩或三個指令，且不會休眠。當這樣的優化無法實現時，
那些函數必須使用框架提供的代碼，那就至少要幾十條指令才可以實現。對於用 GPIO
模擬的 I/O 接口, 如此精簡指令是很有意義的。

對於片上系統，平台特定代碼爲片上 GPIO 每個區(bank)定義並註冊 gpio_chip
實例。那些 GPIO 應該根據晶片廠商的文檔進行編碼/標籤,並直接和電路板原理圖
對應。他們應該開始於零並終止於平台特定的限制。這些 GPIO(代碼)通常從
arch_initcall()或者更早的地方集成進平台初始化代碼，使這些 GPIO 總是可用，
且他們通常可以作爲 IRQ 使用。

板級支持
-------
對於外部 GPIO 控制器(例如 I2C 或 SPI 擴展器、專用晶片、多功能器件、FPGA
或 CPLD)，大多數常用板級特定代碼都可以註冊控制器設備，並保證他們的驅動知道
gpiochip_add()所使用的 GPIO 編號。他們的起始編號通常跟在平台特定的 GPIO
編號之後。

例如板級啓動代碼應該創建結構體指明晶片公開的 GPIO 範圍，並使用 platform_data
將其傳遞給每個 GPIO 擴展器晶片。然後晶片驅動中的 probe()例程可以將這個
數據傳遞給 gpiochip_add()。

初始化順序很重要。例如，如果一個設備依賴基於 I2C 的(擴展)GPIO，那麼它的
probe()例程就應該在那個 GPIO 有效以後才可以被調用。這意味著設備應該在
GPIO 可以工作之後才可被註冊。解決這類依賴的的一種方法是讓這種 gpio_chip
控制器向板級特定代碼提供 setup()和 teardown()回調函數。一旦所有必須的
資源可用之後，這些板級特定的回調函數將會註冊設備，並可以在這些 GPIO 控制器
設備變成無效時移除它們。

用戶空間的 Sysfs 接口(可選)
========================
使用「gpiolib」實現框架的平台可以選擇配置一個 GPIO 的 sysfs 用戶接口。
這不同於 debugfs 接口，因爲它提供的是對 GPIO方向和值的控制，而不只顯示
一個GPIO 的狀態摘要。此外,它可以出現在沒有調試支持的產品級系統中。

例如，通過適當的系統硬體文檔，用戶空間可以知道 GIOP #23 控制 Flash
存儲器的防寫(用於保護其中 Bootloader 分區)。產品的系統升級可能需要
臨時解除這個保護：首先導入一個 GPIO，改變其輸出狀態，然後在重新使能防寫
前升級代碼。通常情況下,GPIO #23 是不會被觸及的，並且內核也不需要知道他。

根據適當的硬體文檔，某些系統的用戶空間 GPIO 可以用於確定系統配置數據，
這些數據是標準內核不知道的。在某些任務中，簡單的用戶空間 GPIO 驅動可能是
系統真正需要的。

注意：標準內核驅動中已經存在通用的「LED 和按鍵」GPIO 任務，分別是:
"leds-gpio" 和 "gpio_keys"。請使用這些來替代直接訪問 GPIO，因爲集成在
內核框架中的這類驅動比你在用戶空間的代碼更好。

Sysfs 中的路徑
--------------
在/sys/class/gpio 中有 3 類入口:

   -	用於在用戶空間控制 GPIO 的控制接口;

   -	GPIOs 本身;以及

   -	GPIO 控制器 ("gpio_chip" 實例)。

除了這些標準的文件,還包含「device」符號連結。

控制接口是只寫的:

    /sys/class/gpio/

    	"export" ... 用戶空間可以通過寫其編號到這個文件，要求內核導出
		一個 GPIO 的控制到用戶空間。

		例如: 如果內核代碼沒有申請 GPIO #19,"echo 19 > export"
		將會爲 GPIO #19 創建一個 "gpio19" 節點。

    	"unexport" ... 導出到用戶空間的逆操作。

		例如: "echo 19 > unexport" 將會移除使用"export"文件導出的
		"gpio19" 節點。

GPIO 信號的路徑類似 /sys/class/gpio/gpio42/ (對於 GPIO #42 來說)，
並有如下的讀/寫屬性:

    /sys/class/gpio/gpioN/

	"direction" ... 讀取得到 "in" 或 "out"。這個值通常運行寫入。
		寫入"out" 時,其引腳的默認輸出爲低電平。爲了確保無故障運行，
		"low" 或 "high" 的電平值應該寫入 GPIO 的配置，作爲初始輸出值。

		注意:如果內核不支持改變 GPIO 的方向，或者在導出時內核代碼沒有
		明確允許用戶空間可以重新配置 GPIO 方向，那麼這個屬性將不存在。

	"value" ... 讀取得到 0 (低電平) 或 1 (高電平)。如果 GPIO 配置爲
		輸出,這個值允許寫操作。任何非零值都以高電平看待。

		如果引腳可以配置爲中斷信號，且如果已經配置了產生中斷的模式
		（見"edge"的描述），你可以對這個文件使用輪詢操作(poll(2))，
		且輪詢操作會在任何中斷觸發時返回。如果你使用輪詢操作(poll(2))，
		請在 events 中設置 POLLPRI 和 POLLERR。如果你使用輪詢操作
		(select(2))，請在 exceptfds 設置你期望的文件描述符。在
		輪詢操作(poll(2))返回之後，既可以通過 lseek(2)操作讀取
		sysfs 文件的開始部分，也可以關閉這個文件並重新打開它來讀取數據。

	"edge" ... 讀取得到「none」、「rising」、「falling」或者「both」。
		將這些字符串寫入這個文件可以選擇沿觸發模式，會使得輪詢操作
		(select(2))在"value"文件中返回。

		這個文件僅有在這個引腳可以配置爲可產生中斷輸入引腳時，才存在。

	"active_low" ... 讀取得到 0 (假) 或 1 (真)。寫入任何非零值可以
		翻轉這個屬性的(讀寫)值。已存在或之後通過"edge"屬性設置了"rising"
		和 "falling" 沿觸發模式的輪詢操作(poll(2))將會遵循這個設置。

GPIO 控制器的路徑類似 /sys/class/gpio/gpiochip42/ (對於從#42 GPIO
開始實現控制的控制器),並有著以下只讀屬性:

    /sys/class/gpio/gpiochipN/

    	"base" ... 與以上的 N 相同,代表此晶片管理的第一個 GPIO 的編號

    	"label" ... 用於診斷 (並不總是只有唯一值)

    	"ngpio" ... 此控制器所管理的 GPIO 數量(而 GPIO 編號從 N 到
    		N + ngpio - 1)

大多數情況下,電路板的文檔應當標明每個 GPIO 的使用目的。但是那些編號並不總是
固定的,例如在擴展卡上的 GPIO會根據所使用的主板或所在堆疊架構中其他的板子而
有所不同。在這種情況下,你可能需要使用 gpiochip 節點(儘可能地結合電路圖)來
確定給定信號所用的 GPIO 編號。

從內核代碼中導出
-------------
內核代碼可以明確地管理那些已通過 gpio_request()申請的 GPIO 的導出:

	/* 導出 GPIO 到用戶空間 */
	int gpio_export(unsigned gpio, bool direction_may_change);

	/* gpio_export()的逆操作 */
	void gpio_unexport();

	/* 創建一個 sysfs 連接到已導出的 GPIO 節點 */
	int gpio_export_link(struct device *dev, const char *name,
		unsigned gpio)

在一個內核驅動申請一個 GPIO 之後，它可以通過 gpio_export()使其在 sysfs
接口中可見。該驅動可以控制信號方向是否可修改。這有助於防止用戶空間代碼無意間
破壞重要的系統狀態。

這個明確的導出有助於(通過使某些實驗更容易來)調試，也可以提供一個始終存在的接口，
與文檔配合作爲板級支持包的一部分。

在 GPIO 被導出之後，gpio_export_link()允許在 sysfs 文件系統的任何地方
創建一個到這個 GPIO sysfs 節點的符號連結。這樣驅動就可以通過一個描述性的
名字，在 sysfs 中他們所擁有的設備下提供一個(到這個 GPIO sysfs 節點的)接口。


code:
.. SPDX-License-Identifier: GPL-2.0

.. raw:: latex

	\renewcommand\thesection*
	\renewcommand\thesubsection*
	\kerneldocCJKon
	\kerneldocBeginTC{

.. _linux_doc_zh_tw:

繁體中文翻譯
============

.. note::
   內核文檔繁體中文版的翻譯工作正在進行中。如果您願意並且有時間參與這項工
   作，歡迎提交補丁給胡皓文 <src.res@email.cn>。

許可證文檔
----------

下面的文檔介紹了Linux內核原始碼的許可證（GPLv2）、如何在原始碼樹中正確標記
單個文件的許可證、以及指向完整許可證文本的連結。

Documentation/translations/zh_TW/process/license-rules.rst

用戶文檔
--------

下面的手冊是爲內核用戶編寫的——即那些試圖讓它在給定系統上以最佳方式工作的
用戶。

.. toctree::
   :maxdepth: 2

   admin-guide/index

TODOList:

* kbuild/index

固件相關文檔
------------

下列文檔描述了內核需要的平台固件相關信息。

TODOList:

* firmware-guide/index
* devicetree/index

應用程式開發人員文檔
--------------------

用戶空間API手冊涵蓋了描述應用程式開發人員可見內核接口方面的文檔。

TODOlist:

* userspace-api/index

內核開發簡介
------------

這些手冊包含有關如何開發內核的整體信息。內核社區非常龐大，一年下來有數千名
開發人員做出貢獻。與任何大型社區一樣，知道如何完成任務將使得更改合併的過程
變得更加容易。

.. toctree::
   :maxdepth: 2

   process/index

TODOList:

* dev-tools/index
* doc-guide/index
* kernel-hacking/index
* trace/index
* maintainer/index
* fault-injection/index
* livepatch/index
* rust/index

內核API文檔
-----------

以下手冊從內核開發人員的角度詳細介紹了特定的內核子系統是如何工作的。這裡的
大部分信息都是直接從內核原始碼獲取的，並根據需要添加補充材料（或者至少是在
我們設法添加的時候——可能不是所有的都是有需要的）。

.. toctree::
   :maxdepth: 2

   cpu-freq/index
   filesystems/index

TODOList:

* driver-api/index
* core-api/index
* locking/index
* accounting/index
* block/index
* cdrom/index
* ide/index
* fb/index
* fpga/index
* hid/index
* i2c/index
* iio/index
* isdn/index
* infiniband/index
* leds/index
* netlabel/index
* networking/index
* pcmcia/index
* power/index
* target/index
* timers/index
* spi/index
* w1/index
* watchdog/index
* virt/index
* input/index
* hwmon/index
* gpu/index
* security/index
* sound/index
* crypto/index
* mm/index
* bpf/index
* usb/index
* PCI/index
* scsi/index
* misc-devices/index
* scheduler/index
* mhi/index

體系結構無關文檔
----------------

TODOList:

* asm-annotations

特定體系結構文檔
----------------

.. toctree::
   :maxdepth: 2

   arm64/index

TODOList:

* arch

其他文檔
--------

有幾份未排序的文檔似乎不適合放在文檔的其他部分，或者可能需要進行一些調整和/或
轉換爲reStructureText格式，也有可能太舊。

TODOList:

* staging/index
* watch_queue

目錄和表格
----------

* :ref:`genindex`

.. raw:: latex

	}\kerneldocEndTC


code:
Chinese translated version of Documentation/driver-api/io_ordering.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.

Traditional Chinese maintainer: Hu Haowen <src.res@email.cn>
---------------------------------------------------------------------
Documentation/driver-api/io_ordering.rst 的繁體中文翻譯

如果想評論或更新本文的內容，請直接聯繫原文檔的維護者。如果你使用英文
交流有困難的話，也可以向繁體中文版維護者求助。如果本翻譯更新不及時或
者翻譯存在問題，請聯繫繁體中文版維護者。

繁體中文版維護者： 胡皓文 Hu Haowen <src.res@email.cn>
繁體中文版翻譯者： 胡皓文 Hu Haowen <src.res@email.cn>
繁體中文版校譯者： 胡皓文 Hu Haowen <src.res@email.cn>

以下爲正文
---------------------------------------------------------------------

在某些平台上，所謂的內存映射I/O是弱順序。在這些平台上，驅動開發者有責任
保證I/O內存映射地址的寫操作按程序圖意的順序達到設備。通常讀取一個「安全」
設備寄存器或橋寄存器，觸發IO晶片清刷未處理的寫操作到達設備後才處理讀操作，
而達到保證目的。驅動程序通常在spinlock保護的臨界區退出之前使用這種技術。
這也可以保證後面的寫操作只在前面的寫操作之後到達設備（這非常類似於內存
屏障操作，mb()，不過僅適用於I/O）。

假設一個設備驅動程的具體例子：

        ...
CPU A:  spin_lock_irqsave(&dev_lock, flags)
CPU A:  val = readl(my_status);
CPU A:  ...
CPU A:  writel(newval, ring_ptr);
CPU A:  spin_unlock_irqrestore(&dev_lock, flags)
        ...
CPU B:  spin_lock_irqsave(&dev_lock, flags)
CPU B:  val = readl(my_status);
CPU B:  ...
CPU B:  writel(newval2, ring_ptr);
CPU B:  spin_unlock_irqrestore(&dev_lock, flags)
        ...

上述例子中，設備可能會先接收到newval2的值，然後接收到newval的值，問題就
發生了。不過很容易通過下面方法來修復：

        ...
CPU A:  spin_lock_irqsave(&dev_lock, flags)
CPU A:  val = readl(my_status);
CPU A:  ...
CPU A:  writel(newval, ring_ptr);
CPU A:  (void)readl(safe_register); /* 配置寄存器？*/
CPU A:  spin_unlock_irqrestore(&dev_lock, flags)
        ...
CPU B:  spin_lock_irqsave(&dev_lock, flags)
CPU B:  val = readl(my_status);
CPU B:  ...
CPU B:  writel(newval2, ring_ptr);
CPU B:  (void)readl(safe_register); /* 配置寄存器？*/
CPU B:  spin_unlock_irqrestore(&dev_lock, flags)

在解決方案中，讀取safe_register寄存器，觸發IO晶片清刷未處理的寫操作，
再處理後面的讀操作，防止引發數據不一致問題。


code:
.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-zh_TW.rst

:Original: :ref:`Documentation/process/1.Intro.rst <development_process_intro>`

:Translator:

 時奎亮 Alex Shi <alex.shi@linux.alibaba.com>

:校譯:

 吳想成 Wu XiangCheng <bobwxc@email.cn>
 胡皓文 Hu Haowen <src.res@email.cn>

.. _tw_development_process_intro:

引言
====

內容提要
--------

本節的其餘部分涵蓋了內核開發的過程，以及開發人員及其僱主在這方面可能遇到的
各種問題。有很多原因使內核代碼應被合併到正式的（「主線」）內核中，包括對用戶
的自動可用性、多種形式的社區支持以及影響內核開發方向的能力。提供給Linux內核
的代碼必須在與GPL兼容的許可證下可用。

:ref:`tw_development_process` 介紹了開發過程、內核發布周期和合併窗口的機制。
涵蓋了補丁開發、審查和合併周期中的各個階段。還有一些關於工具和郵件列表的討論？
鼓勵希望開始內核開發的開發人員跟蹤並修復缺陷以作爲初步練習。

:ref:`tw_development_early_stage` 包括項目的早期規劃，重點是儘快讓開發社區
參與進來。

:ref:`tw_development_coding` 是關於編程過程的；介紹了其他開發人員遇到的幾個
陷阱。也涵蓋了對補丁的一些要求，並且介紹了一些工具，這些工具有助於確保內核
補丁是正確的。

:ref:`tw_development_posting` 描述發布補丁以供評審的過程。爲了讓開發社區能
認真對待，補丁必須被正確格式化和描述，並且必須發送到正確的地方。遵循本節中的
建議有助於確保您的工作能被較好地接納。

:ref:`tw_development_followthrough` 介紹了發布補丁之後發生的事情；工作在這時
還遠遠沒有完成。與審閱者一起工作是開發過程中的一個重要部分；本節提供了一些
關於如何在這個重要階段避免問題的提示。當補丁被合併到主線中時，開發人員要注意
不要假定任務已經完成。

:ref:`tw_development_advancedtopics` 介紹了兩個「高級」主題：使用Git管理補丁
和查看其他人發布的補丁。

:ref:`tw_development_conclusion` 總結了有關內核開發的更多信息，附帶有相關資源
連結。

這個文檔是關於什麼的
--------------------

Linux內核有超過800萬行代碼，每個版本的貢獻者超過1000人，是現存最大、最活躍的
免費軟體項目之一。從1991年開始，這個內核已經發展成爲一個最好的作業系統組件，
運行在袖珍數位音樂播放器、桌上型電腦、現存最大的超級計算機以及所有類型的系統上。
它是一種適用於幾乎任何情況的健壯、高效和可擴展的解決方案。

隨著Linux的發展，希望參與其開發的開發人員（和公司）的數量也在增加。硬體供應商
希望確保Linux能夠很好地支持他們的產品，使這些產品對Linux用戶具有吸引力。嵌入
式系統供應商使用Linux作爲集成產品的組件，希望Linux能夠儘可能地勝任手頭的任務。
分銷商和其他基於Linux的軟體供應商切實關心Linux內核的功能、性能和可靠性。最終
用戶也常常希望修改Linux，使之能更好地滿足他們的需求。

Linux最引人注目的特性之一是這些開發人員可以訪問它；任何具備必要技能的人都可以
改進Linux並影響其開發方向。專有產品不能提供這種開放性，這是自由軟體的一個特點。
如果有什麼不同的話，那就是內核比大多數其他自由軟體項目更開放。一個典型的三個
月內核開發周期可以涉及1000多個開發人員，他們爲100多個不同的公司（或者根本不
隸屬公司）工作。

與內核開發社區合作並不是特別困難。但儘管如此，仍有許多潛在的貢獻者在嘗試做
內核工作時遇到了困難。內核社區已經發展出自己獨特的操作方式，使其能夠在每天
都要更改數千行代碼的環境中順利運行（並生成高質量的產品）。因此，Linux內核開發
過程與專有的開發模式有很大的不同也就不足爲奇了。

對於新開發人員來說，內核的開發過程可能會讓人感到奇怪和恐懼，但這背後有充分的
理由和堅實的經驗。一個不了解內核社區工作方式的開發人員（或者更糟的是，他們
試圖拋棄或規避之）會得到令人沮喪的體驗。開發社區在幫助那些試圖學習的人的同時，
沒有時間幫助那些不願意傾聽或不關心開發過程的人。

希望閱讀本文的人能夠避免這種令人沮喪的經歷。這些材料很長，但閱讀它們時所做的
努力會在短時間內得到回報。開發社區總是需要能讓內核變更好的開發人員；下面的
文字應該幫助您或爲您工作的人員加入我們的社區。

致謝
----

本文檔由Jonathan Corbet <corbet@lwn.net> 撰寫。以下人員的建議使之更爲完善：
Johannes Berg, James Berry, Alex Chiang, Roland Dreier, Randy Dunlap,
Jake Edge, Jiri Kosina, Matt Mackall, Arthur Marsh, Amanda McPherson,
Andrew Morton, Andrew Price, Tsugikazu Shibata 和 Jochen Voß 。

這項工作得到了Linux基金會的支持，特別感謝Amanda McPherson，他看到了這項工作
的價值並將其變成現實。

代碼進入主線的重要性
--------------------

有些公司和開發人員偶爾會想，爲什麼他們要費心學習如何與內核社區合作，並將代碼
放入主線內核（「主線」是由Linus Torvalds維護的內核，Linux發行商將其用作基礎）。
在短期內，貢獻代碼看起來像是一種可以避免的開銷；維護獨立代碼並直接支持用戶
似乎更容易。事實上，保持代碼獨立（「樹外」）是在經濟上是錯誤的。

爲了說明樹外代碼成本，下面給出內核開發過程的一些相關方面；本文稍後將更詳細地
討論其中的大部分內容。請考慮：

- 所有Linux用戶都可以使用合併到主線內核中的代碼。它將自動出現在所有啓用它的
  發行版上。無需驅動程序磁碟、額外下載，也不需要爲多個發行版的多個版本提供
  支持；這一切將方便所有開發人員和用戶。併入主線解決了大量的分發和支持問題。

- 當內核開發人員努力維護一個穩定的用戶空間接口時，內核內部API處於不斷變化之中。
  不維持穩定的內部接口是一個慎重的設計決策；它允許在任何時候進行基本的改進，
  並產出更高質量的代碼。但該策略導致結果是，若要使用新的內核，任何樹外代碼都
  需要持續的維護。維護樹外代碼會需要大量的工作才能使代碼保持正常運行。

  相反，位於主線中的代碼不需要這樣做，因爲基本規則要求進行API更改的任何開發
  人員也必須修復由於該更改而破壞的任何代碼。因此，合併到主線中的代碼大大降低
  了維護成本。

- 除此之外，內核中的代碼通常會被其他開發人員改進。您授權的用戶社區和客戶對您
  產品的改進可能會令人驚喜。

- 內核代碼在合併到主線之前和之後都要經過審查。無論原始開發人員的技能有多強，
  這個審查過程總是能找到改進代碼的方法。審查經常發現嚴重的錯誤和安全問題。
  對於在封閉環境中開發的代碼尤其如此；這種代碼從外部開發人員的審查中獲益匪淺。
  樹外代碼是低質量代碼。

- 參與開發過程是您影響內核開發方向的方式。旁觀者的抱怨會被聽到，但是活躍的
  開發人員有更強的聲音——並且能夠實現使內核更好地滿足其需求的更改。

- 當單獨維護代碼時，總是存在第三方爲類似功能提供不同實現的可能性。如果發生
  這種情況，合併代碼將變得更加困難——甚至成爲不可能。之後，您將面臨以下令人
  不快的選擇：（1）無限期地維護樹外的非標準特性，或（2）放棄代碼並將用戶遷移
  到樹內版本。

- 代碼的貢獻是使整個流程工作的根本。通過貢獻代碼，您可以向內核添加新功能，並
  提供其他內核開發人員使用的功能和示例。如果您已經爲Linux開發了代碼（或者正在
  考慮這樣做），那麼您顯然對這個平台的持續成功感興趣；貢獻代碼是確保成功的
  最好方法之一。

上述所有理由都適用於任何樹外內核代碼，包括以專有的、僅二進位形式分發的代碼。
然而，在考慮任何類型的純二進位內核代碼分布之前，還需要考慮其他因素。包括：

- 圍繞專有內核模塊分發的法律問題其實較爲模糊；相當多的內核版權所有者認爲，
  大多數僅二進位的模塊是內核的派生產品，因此，它們的分發違反了GNU通用公共
  許可證（下面將詳細介紹）。本文作者不是律師，本文檔中的任何內容都不可能被
  視爲法律建議。封閉原始碼模塊的真實法律地位只能由法院決定。但不管怎樣，困擾
  這些模塊的不確定性仍然存在。

- 二進位模塊大大增加了調試內核問題的難度，以至於大多數內核開發人員甚至都不會
  嘗試。因此，只分發二進位模塊將使您的用戶更難從社區獲得支持。

- 對於僅二進位的模塊的發行者來說，支持也更加困難，他們必須爲他們希望支持的
  每個發行版和每個內核版本提供不同版本的模塊。爲了提供較爲全面的覆蓋範圍，
  可能需要一個模塊的幾十個構建，並且每次升級內核時，您的用戶都必須單獨升級
  這些模塊。

- 上面提到的關於代碼評審的所有問題都更加存在於封閉原始碼中。由於該代碼根本
  不可得，因此社區無法對其進行審查，毫無疑問，它將存在嚴重問題。

尤其是嵌入式系統的製造商，可能會傾向於忽視本節中所說的大部分內容；因爲他們
相信自己正在商用一種使用凍結內核版本的獨立產品，在發布後不需要再進行開發。
這個論點忽略了廣泛的代碼審查的價值以及允許用戶向產品添加功能的價值。但這些
產品的商業壽命有限，之後必須發布新版本的產品。在這一點上，代碼在主線上並得到
良好維護的供應商將能夠更好地占位，以使新產品快速上市。

許可
----

代碼是根據一些許可證提供給Linux內核的，但是所有代碼都必須與GNU通用公共許可
證（GPLV2）的版本2兼容，該版本是覆蓋整個內核分發的許可證。在實踐中，這意味
著所有代碼貢獻都由GPLv2（可選地，語言允許在更高版本的GPL下分發）或3子句BSD
許可（New BSD License，譯者注）覆蓋。任何不包含在兼容許可證中的貢獻都不會
被接受到內核中。

貢獻給內核的代碼不需要（或請求）版權分配。合併到主線內核中的所有代碼都保留
其原始所有權；因此，內核現在擁有數千個所有者。

這種所有權結構也暗示著，任何改變內核許可的嘗試都註定會失敗。很少有實際情況
可以獲得所有版權所有者的同意（或者從內核中刪除他們的代碼）。因此，尤其是在
可預見的將來，許可證不大可能遷移到GPL的版本3。

所有貢獻給內核的代碼都必須是合法的免費軟體。因此，不接受匿名（或化名）貢獻
者的代碼。所有貢獻者都需要在他們的代碼上「sign off（簽發）」，聲明代碼可以
在GPL下與內核一起分發。無法提供未被其所有者許可爲免費軟體的代碼，或可能爲
內核造成版權相關問題的代碼（例如，由缺乏適當保護的反向工程工作派生的代碼）
不能被接受。

有關版權問題的提問在Linux開發郵件列表中很常見。這樣的問題通常會得到不少答案，
但請記住，回答這些問題的人不是律師，不能提供法律諮詢。如果您有關於Linux原始碼
的法律問題，沒有什麼可以代替諮詢了解這一領域的律師。依賴從技術郵件列表中獲得
的答案是一件冒險的事情。