[Linux:Kernel] 왜 “volatile” 타입 클래스가 쓰이지 않는 것이 좋을까

이 문서의 저작권은 GPL 라이센스를 따릅니다(This document is released under the GPL license).
Documentation/volatile-considered-harmful.txt
번역: 양정석<dasomoli@gmailREMOVETHIS.com>
왜 “volatile” 타입 클래스가 쓰이지 않는 것이 좋은가
—————————————————
C 프로그래머들은 종종 volatile을 그 변수가 현재 스레드의 실행 밖에서 바뀔 수
있다는 의미로 사용합니다; 그 결과, 그들은 가끔 커널 코드 안에서 공유 자료
구조들을 사용할 때 이를 사용하고자 합니다. 다른 말로 하면, 그들은 간단한
atomic 변수의 일종처럼, 그게 아닌데도, volatile 형식을 다루는 것으로 알고
있습니다. volatile 커널 코드의 용도는 거의 절대 맞지 않습니다; 이 문서는 그
이유를 설명합니다.
volatile에 대한 이해를 위한 핵심은 그 목적이 거의 아무도 절대 진정으로 원하지
않는 최적화를 막기 위해서라는 것입니다. 커널 안에서, 누군가는 공유되는
자료 구조를 매우 다른 태스크의 원치 않는 동시 접근으로부터 보호해야 합니다.
원치 않는 동시성에 대한 보호 처리는 또한 거의 모든 최적화에 관련된 문제를 더
효율적인 방법으로 피할 것입니다.
volatile처럼, 자료로 안전하게 동시 접근을 하는 커널 기본 요소(스핀락(spinlock),
뮤텍스(mutex), 메모리 장벽(memory barrier), 기타 등등)는 원치 않는 최적화를
막도록 설계되었습니다. 그들이 적절히 사용된다면, 그들은 volatile을 사용할
필요가 없을 것입니다. volatile이 여전히 필요하다면, 거기에는 거의 확실히 코드
어딘가에 버그가 있습니다. 적절히 작성된 커널 코드 안에서 volatile은 오직
느리게만 만들 것입니다.
일반적인 커널 코드 블록을 생각해 봅시다:
    spin_lock(&the_lock);
    do_something_on(&shared_data);
    do_something_else_with(&shared_data);
    spin_unlock(&the_lock);
모든 코드가 락킹(locking) 규칙을 따른다면, shared_data의 값은 the_lock이 잡힌
동안, 예기치 않게 바뀔 수 없습니다. 그 자료를 가지고 놀고 싶어 할 어떤 다른
코드는 락 상에서 대기할 것입니다. 스핀락 기본 요소는 자료 접근이 그들을
가로지르는 최적화가 되지 않을 것임을 의미하는 메모리 장벽처럼 – 그들은
명시적으로 그렇게 하도록 작성되었습니다 – 동작합니다. 그래서 컴파일러는 그것이
shared_data 안에 있을 것을 안다고 생각할 수도 있지만. spin_lock() 호출이 메모리
장벽처럼 동작한 이후에는, 컴파일러가 알고 있는 어떠한 것도 잊어버리도록 강제할
것입니다. 거기에는 그 자료로의 접근에 대한 최적화 문제는 없을 것입니다.
shared_data가 volatile로 선언되었어도, 락킹은 여전히 필요할 것입니다.
그러나 컴파일러 역시, 우리가 다른 누구도 그것으로 무언가 할 수 없음을 알 때,
크리티컬 섹션 _안에서는_ shared_data로의 접근에 대한 최적화가 막혔으면 할
것입니다. 락이 잡힌 동안, shared_data는 volatile이 아닙니다. 공유된 자료로
뭔가 할 때, 적절한 락킹은 – 잠재적으로 해로운 – volatile을 불필요하게 만들 것
입니다.
volatile 저장 클래스는 근본적으로 메모리-매핑된(memory-mapped) I/O 레지스터들을
위해 의미가 있습니다. 커널 안에서 레지스터 접근 역시 락으로 보호돼야 합니다만,
누군가는 또한 컴파일러가 크리티컬 섹션 안에서 레지스터 접근을 “최적화하는 것”을
원하지 않습니다. 그러나 커널 안에서, I/O 메모리 접근은 언제나
접근자(accessor) 함수를 통해 이루어집니다; I/O 메모리에 포인터를 통해 직접
접근하는 것은 얼굴이 찡그려지고, 모든 아키텍처에서 작동하지 않습니다.
이들 접근자는 원치 않는 최적화를 막도록 작성되었습니다. 그래서, 역시,
volatile은 불필요합니다.
누군가가 volatile을 사용하고자 하는 또 다른 상황은 프로세서가 한 변수의 값을
위해 바쁜-기다림(busy-waiting)을 할 때입니다. 바쁜 기다림을 수행하는 옳은
방법은:
    while (my_variable != what_i_want)
        cpu_relax();
cpu_relax() 호출은 CPU 전력 소비를 낮출 수 있고, 하이퍼 스레드 된 다른
프로세서에 양보할 수 있습니다; 또한 컴파일러 장벽처럼 서비스하는 일이
일어납니다. 그래서, 역시, volatile은 불필요합니다. 물론, 바쁜 기다림은
일반적으로 시작하는 반사회적 행동입니다.
volatile이 커널 안에서 이해되는 적은 수의 드문 상황들이 여전히 있습니다:
  – 직접 I/O 메모리 접근이 동작하는 아키텍처에서는 위에서 설명된 접근자
    함수들이 volatile을 사용할 수 있습니다. 기본적으로, 각 접근자 호출은
    그 자체로 작은 크리티컬 섹션이 되고, 접근이 프로그래머가 의도한 대로
    일어남을 보장합니다.
  – 메모리를 변경하는, 그렇지만 다른 보이는 부수 효과(side effect)나, GCC에 의해
    제거되는 위험 없는 인라인 어셈블리 코드. volatile 키워드를 asm 문장에
    추가하는 것은 이 제거를 막을 것입니다.
  – jiffies 변수는 그것이 참조되는 순간마다 다른 값을 가지지만, 어떤 특별한
    락킹도 없이 읽힐 수 있어서 특별합니다. 그래서 jiffies는 volatile일 수
    있지만, 이 타입의 다른 변수들의 추가는 강하게 얼굴이 찌푸려집니다.
    jiffies는 이런 점에서 (리누스의 말로) “멍청한 유산” 이슈로 생각됩니다; 이를
    고치는 것은 그 가치보다 더 많은 문제를 일으킬 수 있습니다.
  – 가끔 합법적으로 volatile이 될 수 있는 I/O 장치에 의해 수정될 수 있는
    일관적인(coherent) 메모리 안의 자료 구조로의 포인터. 네트워크 어댑터가 어떤
    기술자(descriptor)가 처리되었는지를 나타내기 위해 포인터를 수정하는 데서,
    그 어댑터에 의해 사용되는 링 버퍼가 이런 종류 상황의 한 예제입니다.
코드 대부분에서, volatile의 적용을 위한 위의 타당한 이유가 없습니다. 그 결과,
volatile의 사용은 버그처럼 보이고, 코드에 추가적인 정밀 조사를 가져오도록 할
가능성이 큽니다. volatile을 사용하고자 하는 개발자들은 발걸음을 돌리고 그들이
정말로 하고자 하는 바에 대해서 생각해 봐야 할 것입니다.
volatile 변수들의 제거 패치는 – 그들이 동시성 이슈들을 적절히 생각했음을
보여주는 타당한 이유로 다가오는 만큼 – 일반적으로 환영받습니다.
참고
—-
[1] http://lwn.net/Articles/233481/
[2] http://lwn.net/Articles/233482/
언급해야 할 이름
—————-
원래의 자극과 연구는 Randy Dunlap에 의해
작성은 Jonathan Corbet에 의해
의견을 통한 개선은 Satyam Sharma, Johannes Stezenbach, Jesper
Juhl, Heikki Orsila, H. Peter Anvin, Philipp Hahn, and Stefan
Richter로부터.
한국어 번역은 양정석이.

[Linux:Kernel] 동적 디버그 사용법(dynamic debug howto.txt)

이 문서의 저작권은 GPL 라이센스를 따릅니다(This document is released under the GPL license).

Documentation/dynamic-debug-howto.txt

번역: 양정석<dasomoli@gmailREMOVETHIS.com>

소개

====

이 문서는 어떻게 동적 디버그 (Dynamic Debug:dyndbg) 기능을 사용하는지 설명합니다.

동적 디버그는 여러분이 추가적인 커널 정보를 얻을 수 있도록 동적으로 커널 코드를 켜고/끌

수 있도록 설계되었습니다. 현재는 CONFIG_DYNAMIC_DEBUG가 설정되어 있다면, 모든

pr_debug()/dev_dbg() 그리고 print_hex_dump_debug()/

print_hex_dump_bytes() 호출은 호출하는 지점마다(per-callsite) 동적으로 켜질

수 있습니다.

CONFIG_DYNAMIC_DEBUG가 설정되어 있지 않으면, print_hex_dump_debug()는

그저 print_hex_dump(KERN_DEBUG)의 축약입니다.

print_hex_dump_debug()/print_hex_dump_bytes()에서 만약 그것이 변하지 않는

문자열이라면 형식 문자열(format string)은 그 ‘prrefix_str’ 인자입니다;

또는 ‘prefix_str’ 이 빌드 시 동적인 경우라면 “hexdump”.

동적 디버그는 더 유용한 기능들도 가집니다:

 * 0이나 1의 조합으로 매칭해서 디버깅 문장을 끄고 켤 수 있도록 하는 간단한 쿼리 언어:

   – 소스 파일 이름

   – 함수 이름

   – (행 번호의 범위를 포함하는) 행 번호

   – 모듈 이름

   – 형식 문자열

 * debugfs 제어 파일 제공: 여러분을 안내하는데 도움이 되도록, 알려진 디버그 문장들의

   완전한 목록을 보이도록 읽혀질 수 있는 <debugfs>/dynamic_debug/control.

동적 디버그 동작 제어하기

=========================

pr_debug()/dev_dbg()의 동작은 이 기능의 용도에 따라 ‘debugfs’ 파일 시스템 내의

제어 파일에 쓰는 것을 통해 제어됩니다. 이어서, 우리는 제어 파일을 알아봅니다:

<debugfs>/dynamic_debug/contorl. 예를 들어, 여러분이 소스 파일 ‘svcsock.c’의

1603번째 행에서 출력을 켜기를 원한다면, 간단히 이렇게 하세요:

nullarbor:~ # echo ‘file svcsock.c line 1603 +p’ >
<debugfs>/dynamic_debug/control

여러분이 문법을 틀린다면, 쓰기가 실패할 것입니다:

nullarbor:~ # echo ‘file svcsock.c wtf 1 +p’ >
<debugfs>/dynamic_debug/control

-bash: echo: write error: Invalid argument

동적 디버그 동작 보기

=====================

여러분은 다음을 통해 현재 설정된 모든 디버그 문장의 동작을 볼 수 있습니다:

nullarbor:~ # cat <debugfs>/dynamic_debug/control

# filename:lineno [module]function flags format

/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:323 [svcxprt_rdma]svc_rdma_cleanup =_ “SVCRDMA Module Removed, deregister RPC RDMA transport\012”

/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:341 [svcxprt_rdma]svc_rdma_init =_ “\011max_inline       : %d\012”

/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:340 [svcxprt_rdma]svc_rdma_init =_ “\011sq_depth         : %d\012”

/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:338 [svcxprt_rdma]svc_rdma_init =_ “\011max_requests     : %d\012”


여러분은 또한 이 데이터에 표준 유닉스 텍스트 조작 필터를 적용할 수 있습니다. 예를 들면,

nullarbor:~ # grep -i rdma <debugfs>/dynamic_debug/control  | wc -l

62

nullarbor:~ # grep -i tcp <debugfs>/dynamic_debug/control | wc -l

42

세번째 열은 현재 각 디버그 문장을 호출하는 곳마다의 켜짐 플래그(이 플래그의 정의는

아래를 보세요)를 보여줍니다. 아무 플래그도 설정되지 않은, 기본 값은 “=_” 입니다.

그래서 여러분은 모든 디버그 문장 호출처를 기본값이 아닌 플래그로 볼 수 있습니다:

nullarbor:~ # awk ‘$3 != “=_”‘ <debugfs>/dynamic_debug/control

# filename:lineno [module]function flags format

/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c:1603 [sunrpc]svc_send p “svc_process: st_sendto returned %d\012”

명령 언어 레퍼런스

==================

어휘적인 수준에서, 하나의 명령은 스페이스나 탭으로 나뉘어진 단어 순서로 구성됩니다.

그래서 이들은 모두 동등합니다:

nullarbor:~ # echo -c ‘file svcsock.c line 1603 +p’ >
<debugfs>/dynamic_debug/control

nullarbor:~ # echo -c ‘  file   svcsock.c     line  1603 +p  ‘ >
<debugfs>/dynamic_debug/control

nullarbor:~ # echo -n ‘file svcsock.c line 1603 +p’ >
<debugfs>/dynamic_debug/control

명령 제출은 write() 시스템 호출로 경계지어집니다. 여러 명령은 ‘;’ 나 ‘\n’ 으로

나뉘어져서 함께 쓰여질 수 있습니다.

  ~# echo “func pnpacpi_get_resources +p; func pnp_assign_mem +p” \

     > <debugfs>/dynamic_debug/control

여러분의 쿼리 집합이 크다면, 역시 이를 일괄적으로 수행할 수 있습니다:

  ~# cat query-batch-file > <debugfs>/dynamic_debug/control

다른 방법은 와일드카드를 사용하는 것입니다. 매치 규칙은 ‘*’ (0개나 그 이상의 문자들과

매치)과 ‘?’ (정확히 하나의 문자와 매치)를 지원합니다. 예를 들면, 여러분은 모든 usb

드라이버들을 매치시킬 수 있습니다:

  ~# echo “file drivers/usb/* +p” > <debugfs>/dynamic_debug/control

문법적인 수준에서, 매치 명세(match-spec)들, 그 뒤에 플래그 변경 명세(flags-spec)

의 순서로 구성됩니다.

command ::= match-spec* flags-spec

매치 명세들은 플래그 변경 명세를 어디에 적용해야 하는지를 위해 알려진 pr_debug()

호출처의 부분 집합을 고르는데 사용됩니다. 각 짝들 사이에 암묵적인 AND로 하나의 쿼리처럼

그들을 생각하세요. 매치 명세의 빈 목록은 모든 디버그 문장 호출처를 선택함을 알아두세요.

매치 명세는 비교되는 호출처의 속성을 제어하는 하나의 키워드, 그리고 비교될 하나의 값으로

구성됩니다. 가능한 키워드들은:

match-spec ::= ‘func’ string |
      ‘file’ string |
      ‘module’ string |
      ‘format’ string |
      ‘line’ line-range

line-range ::= lineno |
      ‘-‘lineno |
      lineno’-‘ |
      lineno’-‘lineno

// 알림: line-range 스페이스를 포함할 수 없습니다. 예를 들면,

// “1-30″은 유효한 범위이지만, “1 – 30″은 아닙니다.

lineno ::= unsigned-int

각 키워드의 뜻은:

func

    주어진 문자열은 각 호출처의 함수 이름과 비교됩니다 예를 들면:

    func svc_tcp_accept

file

    주어진 문자열은 전체 경로 이름이나 소스 루트의(src-root) 상대 경로 이름, 또는 각

    호출처의 소스 파일의 경로를 제외한 이름(basename)과 비교됩니다. 예를 들면:

    file svcsock.c

    file kernel/freezer.c

    file /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c

module

    주어진 문자열은 각 호출처의 모듈 이름과 비교됩니다. 모듈 이름은 “lsmod” 에서 보여지는

    문자열입니다. 즉, 디렉토리나 .ko 접미사가 없고, ‘-‘는 ‘_’로 변경됩니다. 예를 들면:

    module sunrpc

    module nfsd

format

    주어진 문자열은 동적 디버그 형식 문자열 내에서 검색됩니다. 그 문자열은 전체 형식과

    매치될 필요는 없고 오직 일부만 매치되면 된다는 것을 알아두세요. 공백 문자들과 다른

    특수 문자들은 C의 8진수 문자 이스케이프 \ooo 표현을 사용해서 쓰여질 수 있습니다.

    예를 들면, 스페이스 문자는 \040 입니다. 다른 방식으로, 문자열을 쌍따옴표 문자(“) 나

    따옴표(‘)로 둘러쌀 수 있습니다.

    예제:

    format svcrdma:
   // NFS/RDMA 서버 pr_debug 들에서 많음

    format readahead
   // readahead 캐시 안의 pr_debug 들

    format nfsd:\040SETATTR // 공백 문자로 형식을 매치하는 한가지 방법

    format “nfsd: SETATTR”  // 공백 문자로 형식을 매치하는 더 깔끔한 방법

    format ‘nfsd: SETATTR’  // 공백 문자로 형식을 매치하는 또 다른 방법

line

    주어진 행 번호 또는 행 번호의 범위는 각 pr_debug() 호출처의 행 번호와 비교됩니다.

    하나의 행 번호는 호출처 행 번호와 정확히 매치됩니다. 행 번호의 범위는 처음과 마지막의

    행 번호 안의 호출처와 매치됩니다. 첫번째 수가 없는 것은 파일의 첫번째 줄을 의미하고,

    행 번호가 없는 것은 파일의 마지막 줄을 의미합니다. 예를 들면: 

    line 1603
   // 정확히 1603번 줄

    line 1600-1605  // 1600번에서 1605번 줄까지의 여섯 줄

    line -1605
   // 1번 줄에서 1605번 줄까지의 1605 줄

    line 1600-
   // 1600번 줄에서 파일의 끝까지의 모든 줄

플래그 명세는 하나 또는 그 이상의 플래그 성질이 따르는 하나의 변경 동작으로 구성됩니다.

변경 동작은 하나의 성질입니다:

  –    주어진 플래그 제거

  +    주어진 플래그 추가

  =    주어진 플래그로 플래그를 설정

플래그들은 다음과 같습니다:

  p    pr_debug() 호출처 켜기

  f    출력된 메시지 안에 함수 이름을 포함

  l    출력된 메시지 안에 행 번호를 포함

  m    출력된 메시지 안에 모듈 이름을 포함

  t    인터럽트 컨텍스트에서 생성되지 않은 메시지 안에 스레드 ID 포함.

  _    플래그가 설정되지 않음. (또는 입력 상의 다른 것들로 설정)

print_hex_dump_debug()와 print_hex_dump_bytes()에서는, 오직 ‘p’ 플래그가

다른 플래그들이 무시되는 것을 의미합니다.

보여줄 때, 플래그들은 ‘=’이 앞에 옵니다(연상 기호: 무슨 플래그가 현재 동일한지).

Note the regexp ^[-+=][flmpt_]+$ matches a flags specification.

To clear all flags at once, use “=_” or “-flmpt”.

정규표현 ^[-+=][flmpt_]+$ 는 플래그 명세와 매치함을 알아두세요.

모든 플래그를 없애기 위해서, “=_” 나 “-flmpt”를 사용하세요.

부트 프로세스 동안의 디버그 메시지

==================================

부트 프로세스 동안의 코어 코드와 빌트인 모듈 디버그 메시지를 활성화 하기 위해서, 유저 공간과

debugfs 가 존재하기 전에도, dyndbg=”QUERY”, module.dyndbg=”QUERY”, 또는

ddebug_query=”QUERY”(ddebug_query는 dyndbg에 의해 구식이 되었고, 더 이상 사용하지

않습니다)를 사용할 수 있습니다. QUERY는 위에 설명된 문법을 따르지만, 1023 문자를 넘을 수

없습니다. 여러분의 부트로더는 제한으로 더 적은 수를 쓰고 있을 수 있습니다.

이들 dyndbg 파라미터들은 ddebug 테이블이 처리된 후에, arch_initcall 의 일부로

바로 처리됩니다. 그래서 여러분은 이 부트 파라미터를 통해 이 arch_initcall 이후에

모든 코드 안의 디버그 메시지를 켤 수 있습니다.

예를 들면, x86 시스템 상에서 ACPI 활성화는 subsys_initcall 이고,

   dyndbg=”file ec.c +p”

는 여러분의 머신(일반적으로 랩탑)이 임베디드 컨트롤러를 가진다면, ACPI 셋업동안 초기 임베디드

컨트롤러 트랜잭션을 보여줄 것입니다.

PCI (또는 다른 장치들의) 초기화는 또한 디버깅 목적의 이 부트 파라미터 사용의 유력한

후보들입니다.

foo 모듈이 빌트-인이 아니라면, foo.dyndbg 는 여전히 부트 타임에 아무 효과 없이 처리될

것입니다만, 모듈이 이후에 로딩될 때 처리될 것입니다. dyndbg_query= 과 순수한 dyndbg= 은

부트 시에만 처리됩니다.

모듈 초기화 시점의 디버그 메시지

================================

“modprobe foo”가 호출될 때, modprobe는 foo.params를 위해 /proc/cmdline을 스캔하고,

“foo.”을 벗겨서 제거하고, 이를 modprobe args 나 /etc/modprob.d/*.conf 파일 안에

주어진 파라미터들과 함께 다음 순서로 커널에게 넘긴다:

1. # /etc/modprobe.d/*.conf를 통해 주어진 파라미터들

   options foo dyndbg=+pt

   options foo dyndbg # +p로 기본

2. # boot args에서 주어진 foo.dyndbg, “foo.” 는 벗겨져서 제거되어 전달됨

   foo.dyndbg=” func bar +p; func buz +mp”

3. # modprobe로의 args

   modprobe foo dyndbg==pmf # 이전 설정을 덮어 씀

이들 dyndbg 쿼리들은 가장 마지막에 말한 것이 마지막에 적용되도록, 순서대로 적용됩니다.

이것은 boot args가 /etc/modprobe.d(정확하게는 1이 시스템 전체, 2는 커널이나 부트에

의존적임)로부터 이들을 수정하거나 덮어 쓰도록, 그리고 modeprbe args 가 둘 다를 덮어쓰도록

합니다.

foo.dyndbg=”QUERY” 형식 안에서, 쿼리는 반드시 “foo 모듈”을 제외해야 합니다.

“foo”는 param-name 으로부터 추출되고, “QUERY” 내의 각 쿼리에 적용되고, 오직

각 타입의 1 match-spec만 적용됩니다.

dyndbg 옵션은 “가짜” 모듈 파라미터입니다. 이는 다음을 의미합니다:

– 모듈들은 그를 명시적으로 정의할 필요가 없습니다.

– 모든 모듈은 그들이 pr_debug를 사용하든 말든, 아무 말 없이 이를 얻을 수 있습니다.

– 그것은 /sys/module/$module/parameters 안에 나타나지 않습니다. 이를 보기 위해서,

  제어 파일을 grep 하거나, /proc/cmdline 을 검사하세요.
CONFIG_DYNAMIC_DEBUG 커널에서는 디버그 메시지가 더이상 필요하지 않다면, 부트-타임에
주어진 (또는 컴파일 동안 -DDEBUG 플래그에 의해 켜진) 어떠한 셋팅도 이후에 sysfs
인터페이스를 통해 끌 수 있습니다.

   echo “module module_name -p” > <debugfs>/dynamic_debug/control

예제

====

// svcsock.c 파일의 1603번째 줄의 메시지를 켜기

nullarbor:~ # echo -n ‘file svcsock.c line 1603 +p’ >
<debugfs>/dynamic_debug/control

// svcsock.c 파일 안의 모든 메시지 켜기

nullarbor:~ # echo -n ‘file svcsock.c +p’ >
<debugfs>/dynamic_debug/control

// NFS 서버 모듈의 모든 메시지 켜기

nullarbor:~ # echo -n ‘module nfsd +p’ >
<debugfs>/dynamic_debug/control

// svc_process() 함수 안의 모든 12 메시지를 켜기

nullarbor:~ # echo -n ‘func svc_process +p’ >
<debugfs>/dynamic_debug/control

// svc_process() 함수 안의 모든 12 메시지를 끄기

nullarbor:~ # echo -n ‘func svc_process -p’ >
<debugfs>/dynamic_debug/control

// NFS가 호출하는 READ, READLINK, READDIR 그리고, READDIR+를 위한 메시지 켜기

nullarbor:~ # echo -n ‘format “nfsd: READ” +p’ >
<debugfs>/dynamic_debug/control

// “usb” 문자열을 포함하는 경로의 메시지 켜기

nullarbor:~ # echo -n ‘*usb* +p’ > <debugfs>/dynamic_debug/control

// 모든 메시지 켜기

nullarbor:~ # echo -n ‘+p’ > <debugfs>/dynamic_debug/control

// 모든 켜진 메시지에 모듈과 함수 추가

nullarbor:~ # echo -n ‘+mf’ > <debugfs>/dynamic_debug/control

// boot-args 예제, 가독성을 위해 줄바꿈과 주석을 넣음

Kernel command line: …

  // dyndbg=값 처리 안에서 어떻게 되어가고 있는지 보기

  dynamic_debug.verbose=1

  // 2 빌트인 안의 pr_debug 켜기, #cmt 는 제거됨

  dyndbg=”module params +p #cmt ; module sys +p”

  // 나중에 로딩되는 모듈 안의 2 함수 안의 pr_debug를 켜기

  pc87360.dyndbg=”func pc87360_init_device +p; func pc87360_find +p”

[Linux:Kernel] 커널 메모리 누수 검출기

이 문서의 저작권은 GPL 라이센스를 따릅니다(This document is released under the GPL license).

Documentation/kmemleak.txt

번역: 양정석(dasomoli@gmailREMOVETHIS.com)

커널 메모리 누수 검출기
=======================

소개
—-

Kmemleak은 주인없는 객체들을 해제하지는 않고 그저 /sys/kernel/debug/kmemleak을
통해 보고하는 차이가 있는 가비지 컬렉터 추적
(http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29#Tracing_garbage_collectors)
과 비슷한 방법으로, 가능한 커널 메모리 누수를 검출하는 방법을 제공합니다. 유저-공간의
애플리케이션에서 메모리 누수를 검출하는 데 Valgrind 도구(memcheck –leak-check)가
유사한 방법을 사용합니다.

지원되는 아키텍처를 위해 lib/Kconfig.debug 안의 DEBUG_KMEMLEAK 의존성을 체크하세요.

사용법
——

“Kernel hacking” 안의 CONFIG_DEBUG_KMEMLEAK이 켜져야 합니다. 한 커널 스레드가
메모리를 (기본값으로) 매 10분마다 스캔하고, 찾은 새로운 참조되지 않는 객체들의
수를 출력합니다. 모든 가능한 메모리 누수의 자세할 사항을 보기 위해서는:

  # mount -t debugfs nodev /sys/kernel/debug/
  # cat /sys/kernel/debug/kmemleak

중간에 메모리 스캔을 작동시키려면:

  # echo scan > /sys/kernel/debug/kmemleak

모든 현재의 가능한 메모리 누수의 목록을 지우려면:

  # echo clear > /sys/kernel/debug/kmemleak

그려면 새로운 누수는 /sys/kernel/debug/kmemleak을 다시 읽음으로써 나올 것입니다.

주인없는 객체들은 그들이 할당되었던 순서로 나열되고, 목록의 맨 앞의 한 객체는
주인없는 것으로 보고되는 다른 이어지는 객체들의 원인이 될 것입니다.

메모리 스캐닝 파라미터들은 런타임에 /sys/kernel/debug/kmemleak 파일에 씀으로써
바뀔 수 있습니다. 다음 파라미터들이 지원됩니다:

  off           – kmemleak 끄기 (되돌릴 수 없음)
  stack=on      – 태스크 스택 스캐닝 켜기 (기본값)
  stack=off     – 태스크 스택 스캐닝 끄기
  scan=on       – 자동 메모리 스캐닝 스레드 시작하기 (기본값)
  scan=off      – 자동 메모리 스캐닝 스레드 멈추기
  scan=<secs>   – 자동 메모리 스캐닝 주기를 초로 설정
                  (기본 600, 0은 자동 스캐닝 멈춤)
  scan          – 한 번의 메모리 스캔 작동
  clear         – 현재 메모리 누수 혐의자들의 목록 지우기, 모든 현재 보고된
                  참조되지 않는 객체를 회색으로 표시함으로써 끝
  dump=<addr>   – <주소>에서 찾아진 객체에 대한 정보를 덤프

Kmemleak은 또한 부팅 시간에 “kmemleak=off”를 커널 커맨드 라인에 전달함으로써
꺼질 수도 있습니다.

kmemleak이 초기화되기 전에 메모리는 할당되거나 해제될 것이고, 이들 동작은 초기
로그 버퍼 안에 저장될 것입니다.이 버퍼의 크기는
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE 옵션을 통해 설정됩니다.

기본 알고리즘
————-

kmalloc, vmalloc, kmem_cache_alloc과 그 친구들을 통한 메모리 할당은 추적되고,
그 포인터들은, 크기나 스택 추적같은 추가 정보와 함께 prio 검색 트리 안에
저장됩니다. 그에 맞는 해제 함수 호출들은 기록되고 그 포인터들은 kmemleak 자료
구조로부터 제거됩니다.

메모리의 할당된 블록이 (저장된 레지스터들을 포함하는) 메모리 스캐닝을 통해
그 시작 주소나 블록 안의 어떤 위치로의 어떤 포인터도 찾을 수 없다면, 주인없는
것으로 여겨집니다. 이것은 커널이 할당된 블록의 주소를 해제하는 함수로 넘길 수
있는 방법이 없을 것이고, 그래서 그 블록이 메모리 누수라고 여겨진다는 것을
의미합니다.


스캐닝 알고리즘 단계:

  1. 모든 객체를 하얗게 표시 (남은 하얀 객체들은 나중에 주인없는 것으로 여겨질
     것 입니다.)
  2. 데이터 섹션과 스택들의 메모리 시작을 스캔, prio 검색 트리 안에 저장된 주소들에
     대조되는 값을 검사. 하얀 객체로의 포인터가 있다면, 그 객체는 회색 목록에
     추가합니다.
  3. 회색 집합이 끝날 때까지 매칭되는 주소들을 위해 회색 객체들을 스캔(어떤
     하얀 객체들은 회색이 되고, 회색 목록의 끝에 추가됩니다)
  4. 남아있는 하얀 객체들은 주인없는 것으로 여겨지고, /sys/kernel/debug/kmemleak
     을 통해 보고됩니다.

어떤 할당된 메모리 블록들은 커널의 내부 자료 구조 안에 저장된 포인터들을 가지고,
그들은 주인없는 것으로 검출될 수 없습니다. 이를 피하기 위해서, kmemleak은
또한, 찾아질 필요가 있는 블록 주소 범위 내의 주소를 가리키는 값의 수를 저장할
수 있어서 그 블록은 누수로 여겨지지 않습니다. 한 예로 __vmalloc()이 있습니다.

kmemleak으로 특정 섹션 테스트하기
———————————

초기 부팅 상에서 여러분의 /sys/kernel/debug/kmemleak 출력 페이지는 좀
많을 것입니다. 이것은 또한 여러분이 개발을 할 때 매우 버그가 많은 코드를
가진다면 있을 수 있습니다. 이런 상황에서 일하기 위해서
/sys/kernel/debug/kmemleak 출력으로부터 모든 보고된 참조되지 않는 객체를
지우기 위해서 여러분은 ‘clear’ 명령을 사용할 수 있습니다. ‘clear’ 후의
‘scan’을 통해, 여러분은 새로운 참조되지 않는 객체들을 찾을 수 있습니다;
이것은 코드의 특정 섹션을 테스팅하는 데 도움이 될 것입니다.

깨끗한 kmemleak으로 하고 싶을 때 크리티컬 섹션을 테스트하기 위해서는
이렇게 하세요:

  # echo clear > /sys/kernel/debug/kmemleak
  … 여러분의 커널이나 모듈을 테스트 …
  # echo scan > /sys/kernel/debug/kmemleak

그 후, 여느 때처럼 여러분의 보고서를 얻기 위해서:

  # cat /sys/kernel/debug/kmemleak

Kmemleak API
————

함수들의 프로토 타입은 /include/linux/kmemleak.h 헤더를 보세요.

kmemleak_init            – kmemleak 초기화
kmemleak_alloc           – 하나의 메모리 블록 할당 알림
kmemleak_alloc_percpu    – 하나의 percpu 메모리 블록 할당 알림
kmemleak_free            – 하나의 메모리 블록 해제 알림
kmemleak_free_part       – 하나의 부분 메모리 블록 해제 알림
kmemleak_free_percpu     – 하나의 percpu 메모리 블록 해제 알림
kmemleak_not_leak        – 누수가 아닌 것으로 객체 표시
kmemleak_ignore          – 스캔하지 않거나, 누수로 객체를 보고하지 않음
kmemleak_scan_area       – 메모리 블록내의 스캔 지역 추가
kmemleak_no_scan         – 메모리 블록 스캔하지 않음
kmemleak_erase           – 포인터 변수 안의 이전 값을 지움
kmemleak_alloc_recursive – kmemleak_alloc과 같지만 재귀성 검사
kmemleak_free_recursive  – kmemleak_free와 같지만 재귀성 검사

거짓 검출/미검출의 처리
———————–

거짓 미검출은 진짜 메모리 누수(주인없는 개체)가 메모리 스캐닝 중에 찾은 값들이
객체들을 가리키고 있어서 kmemleak에 의해 보고되지 않는 것입니다.
거짓 미검출의 수를 줄이기 위해서, kmemleak은 kmemleak_ignore,
kmemleak_scan_area, kmemleak_no_scan, 그리고 kmemleak_erase 함수들
(위를 보세요)을 제공합니다. 태스크 스택은 또한 거짓 미검출의 양을 늘려서,
그들의 스캐닝은 기본값으로 켜지지 않습니다.

거짓 검출은 객체들이 메모리 누수(주인없음)이 된 것처럼 잘못 보고되는 것입니다.
누수가 아닌 것으로 알려진 객체들을 위해서, kmemleak은 kmemleak_not_leak
함수를 제공합니다. kmemleak_ignore 또한 그 메모리 블록이 다른 포인터들을
포함하는 것으로 알려지지 않았다면 사용될 수 있고, 더 이상 스캔되지 않을
것 입니다.

보고된 누수들 중 어떤 것들은 CPU 레지스터나 스택 안에 임시적으로 저장되는
포인터들 때문에, 특히 SMP 시스템 상에서, 그저 일시적입니다. Kmemleak은
메모리 누수로 보고되는 객체의 최소 나이를 표시하는 (기본은 1000인)
MSECS_MIN_AGE를 정의합니다.

제한과 결점
———–

주요 결점은 메모리 할당과 해제의 성능을 떨어뜨린 다는 것 입니다. 다른 불이익을
피하기 위해서, 메모리 스캐닝은 /sys/kernel/debug/kmemleak 파일을 읽을 때만
수행됩니다. 어쨌든, 이 도구는 성능이 가장 중요한 요구 사항이 아닌 곳에서의
디버깅을 목적으로 합니다.

알고리즘을 간단히 유지하기 위해서, kmemleak은 한 블록의 주소 범위 안의
어떤 주소를 가리키는 값들을 스캔합니다. 이것은 거짓 미검출의 수를 늘릴 것입니다.
그러나, 그것은 진짜 메모리 누수가 결국 보여지게 되도록 할 것입니다.

거짓 미검출의 다른 출처는 포인터가 아닌 값들안에 저장된 데이터입니다. 이후
버전에서는 kmemleak이 할당된 구조체 안의 포인터 멤버만 스캔할 수 있을 것입니다.
이 기능은 위에서 설명한 많은 거짓 미검출을 해결할 것 입니다.

이 도구는 거짓 검출을 보고할 수 있습니다. 할당된 블록이 해제를 필요로 하지 않는
곳(init_call 함수 안의 어떠한 경우), 그 포인터가 보통의 container_of
매크로가 아닌 다른 방법으로 계산되는 곳, 또는 그 포인터가 kmemleak이 스캔하지
않는 위치 안에 저장된 포인터인 곳 같은 경우들이 있습니다.

페이지 할당과 ioremap은 추적하지 않습니다.

[Linux:Kernel] PM Quality Of Service 인터페이스

이 문서의 저작권은 GPL 라이센스를 따릅니다(This document is released under the GPL license).
Documentation/power/pm_qos_interface.txt
번역: 양정석(dasomoli@gmailREMOVETHIS.com)
PM Quality Of Service 인터페이스.
이 인터페이스는 커널과 유저 모드에 그 파라미터 중 하나로 드라이버, 서브 시스템,
그리고 유저 공간 애플리케이션들에 의한 성능 기대 사항을 등록하기 위한
인터페이스를 제공합니다.
두가지 다른 PM QoS 프레임워크가 이용가능합니다:
1. cpu_dma_latency, network_latency, network_throughput을 위한 PM QoS 클래스들.
2. per-device PM QoS 프레임워크는 per-device 지연 제약 사항과 PM QoS 플래그를
관리하기 위한 API 를 제공합니다.
각 파라미터는 단위를 정의했습니다:
 * 지연(latency): usec
 * 만료(timeout): usec
 * 처리용량: kbs (킬로 비트 / 초)
1. PM QoS framework
이 인프라 구조는 여러개의 misc 디바이스 노드들을 구현된 파라미터 당 하나로
노출합니다. 파라미터들 구현 집합은 pm_qos_power_init() 과 pm_qos_params.h 에
의해 정의됩니다. 드라이버로부터 런타임에 설정 가능하거나 변경 가능한 사용 가능
파라미터들을 갖는 것이 오용되기 너무 쉬운 것으로 보여지기 때문에 이렇게
수행됩니다.
각 파라미터를 위해 성능 요구에 대한 리스트가 취합된(aggregated) 목표 값으로
관리됩니다. 취합된 목표 값은 그 요구 목록이나 그 리스트의 개체들로의 변경으로
갱신됩니다. 일반적으로 그 취합된 목표 값은 간단히 파라미터 리스트 개체들 안에
잡힌 요청 값의 최고 또는 최저 값입니다.
알림: 그 취합된 목표 값은 어토믹(atomic) 변수로 구현됩니다. 그래서 취합된 값을
읽는 것은 어떤 락킹 메카니즘도 필요 없습니다.
커널 모드로부터 이 인터페이스의 사용은 간단합니다:
void pm_qos_add_request(handle, param_class, target_value):
는 개체 하나를 그 리스트 안에 목표 값으로 식별된 PM QoS 클래스를 위해 삽입할
것입니다. 이 리스트로의 변경 위에서 새로운 목표는 재계산되고, 어떤 등록된
통지자(notifier)가 그 목표 값이 지금과 다를 때만 호출됩니다. pm_qos의
클라이언트는 다른 pm_qos API 함수들 안에서 추후에 사용될 때를 위해 반환된
핸들을 저장할 필요가 있습니다.
void pm_qos_update_request(handle, new_target_value):
는 그 핸들이 가리키는 리스트 개체를 새로운 목표 값으로 갱신하고, 그 목표가
바뀌면 취합된 목표, 통지(notifictation) 트리 호출을 재계산합니다.
void pm_qos_remove_request(handle):
는 개체를 제거합니다. 제거 후에는 취합된 목표를 갱신하고, 그 목표가 요청 제거의 결과로 바뀌면, 통지 트리를 호출할 것입니다.
int pm_qos_request(param_class):
는 주어진 PM QoS 클래스를 위한 취합된 값을 반환합니다.
int pm_qos_request_active(handle):
는 그 요청이 여전히 살아있는지를 반환합니다. 즉, PM QoS 클래스 제약 사항 리스트
로부터 제거되지 않았다는 것입니다.
int pm_qos_add_notifier(param_class, notifier):
는 통지 콜백 함수를 PM QoS 클래스로 추가합니다. 그 콜백은 PM QoS 클래스를 위한
취합된 값이 바뀌었을 때 호출됩니다.
int pm_qos_remove_notifier(int param_class, notifier):
는 PM QoS 클래스를 위한 통지 콜백 함수를 제거합니다.
유저 모드에서는:
프로세스들만 pm_qos 요청을 등록할 수 있습니다. 프로세스의 자동적인 청소 작업을
제공하기 위해서, 인터페이스는 프로세스에게 다음과 같은 방법으로 그 파라미터
요청을 등록하도록 요구합니다:
지정된 파라미터의 기본 pm_qos 목표를 등록하기 위해서, 프로세스는
/dev/[cpu_dma_latency, network_latency, network_throughput] 중 하나를
열어야만 합니다.
디바이스 노드가 연 것을 유지하는 동안 그 프로세스는 파라미터 상의 등록된
요청을 가집니다.
요청된 목표 값을 변경하기 위해서, 그 프로세스는 s32 값을 열린 디바이스 노드에
쓸 필요가 있습니다. 아니면, 유저 모드 프로그램이 16진수 문자열을 10 글자 길이의
형식의 값, 예를 들면, “0x12345678″로 쓸 수 있습니다. 이것은 pm_qos_update_request
호출로 변환됩니다.
목표 값의 유저 모드 요청을 지우려면, 간단히 디바이스 노드를 닫으세요.
2. PM QoS per-device 지연 프레임워크
각 개별 디바이스를 위한 성능 요청의 리스트는 취합된 목표 값으로 관리됩니다.
취합된 목표 값은 요청 리스트나 그 리스트의 개체로의 변경으로 갱신됩니다.
일반적으로 그 취합된 목표 값은 간단히 파라미터 리스트 개체 안에 잡힌 요청 값의
최고, 또는 최소 값입니다.
알림: 그 취합된 목표 값은 어토믹(atomic) 변수로 구현됩니다. 그래서 취합된 값을
읽는 것은 어떤 락킹 메카니즘도 필요 없습니다.
커널 모드로부터 이 인터페이스 사용은 다음과 같습니다:
int dev_pm_qos_add_request(device, handle, value):
는 개체 하나를 그 리스트 안으로 목표 값으로 식별된 디바이스를 위해 삽입할
것입니다. 이 리스트로의 변경 위해서 새로운 목표는 재계산되고, 어떤 등록된
통지자가 그 목표 값이 지금과 다를 때만 호출됩니다. dev_pm_qos의 클라이언트는
다른 dev_pm_qos API 함수들 안에서 추후에 사용될 때를 위해 그 핸들을 저장할
필요가 있습니다.
int dev_pm_qos_update_request(handle, new_value):
는 그 핸들이 가리키는 리스트 개체를 새로운 목표 값으로 갱신하고, 그 목표가
바뀌면 새로운 취합된 목표, 통지 트리 호출을 재계산합니다.
int dev_pm_qos_remove_request(handle):
는 개체를 제거합니다. 제거 후에는 취합된 목표를 갱신하고, 그 목표가 요청 제거의
결과로 바뀌면, 통지 트리를 호출할 것입니다.
s32 dev_pm_qos_read_value(device):
주어진 디바이스의 제약 사항 라스트를 위한 취합 값을 반환합니다.
통지 메카니즘:
per-device PM QoS 프레임워크는 2개의 다르고 구별되는 통지 트리를 가집니다:
per-device 통지 트리와 전역 통지 트리
int dev_pm_qos_add_notifier(device, notifier):
디바이스를 위한 통지 콜백 함수를 추가합니다.
디바이스 제약사항 리스트의 취합된 값이 바뀔 때, 콜백이 호출됩니다.
int dev_pm_qos_remove_notifier(device, notifier):
디바이스를 위한 통지 콜백 함수를 제거합니다.
int dev_pm_qos_add_global_notifier(notifier):
프레임워크의 전역 통지 트리 안에 통지 콜백 함수를 추가합니다.
어떤 디바이스를 위한 취합된 값이 바뀔 때, 콜백이 호출됩니다.
int dev_pm_qos_remove_global_notifier(notifier):
프레임워크의 전역 통지 트리로부터 통지 콜백 함수를 제거합니다.
유저모드에서는:
per-device 지연 제약 사항으로의 유저 공간 접근을 위한 API는 아직 제공되지
않습니다. – 아직 토의 중.

[Linux:Kernel] CPU 토폴로지

이 문서의 저작권은 GPL 라이센스를 따릅니다(This document is released under the GPL license).

Documentation/cputopology.txt

번역: 양정석(dasomoli@gmailREMOVETHIS.com)

CPU 토폴로지는 sysfs를 통해 노출됩니다. 항목(속성들)은 /proc/cpuinfo와
유사합니다.

1) /sys/devices/system/cpu/cpuX/topology/physical_package_id:

        cpuX의 물리적인 패키지 ID. 일반적으로 물리적인 소켓 번호에
        해당하지만, 실제 값은 아키텍처와 플랫폼에 따라 다릅니다.

2) /sys/devices/system/cpu/cpuX/topology/core_id:

        cpuX의 CPU 코어 ID. 일반적으로 (커널의 것 보다는) 하드웨어
        플랫폼의 식별자입니다. 실제 값은 아키텍처와 플랫폼에 따라
        다릅니다.

3) /sys/devices/system/cpu/cpuX/topology/book_id:

        cpuX의 Book ID. 일반적으로 (커널의 것 보다는) 하드웨어 플랫폼의
        식별자입니다. 실제 값은 아키텍처와 플랫폼에 따라 다릅니다.

4) /sys/devices/system/cpu/cpuX/topology/thread_siblings:

        cpuX와 같은 코어 내부의 cpuX의 하드웨어 스레드들의 내부 커널 맵

5) /sys/devices/system/cpu/cpuX/topology/core_siblings:

        같은 물리적 패키지 ID(physical_package_id) 내부의 cpuX의
        하드웨어 스레드들의 내부 커널 맵

6) /sys/devices/system/cpu/cpuX/topology/book_siblings:

        같인 Book ID(book_id) 내부의 cpuX의 하드웨어 스레드들의 내부
        커널 맵

아키텍처에 자연스러운 방법으로 구현하기 위해서, 새로운 소스 파일,
drivers/base/topology.c는 4 에서 6 개의 속성들을 드러냅니다. sysfs
파일들에 관련된 두 book 은 CONFIG_SCHED_BOOK 이 선택되었을 경우에만
생성될 것입니다.

이 기능을 지원하는 아키텍처를 위해서, include/asm-XXX/topology.h 안에
이들 매크로들 중 몇가지가 정의되어야만 합니다:
#define topology_physical_package_id(cpu)
#define topology_core_id(cpu)
#define topology_book_id(cpu)
#define topology_thread_cpumask(cpu)
#define topology_core_cpumask(cpu)
#define topology_book_cpumask(cpu)

**_id의 타입은 int 입니다.
sibling의 타입은 (const) struct cpumask * 입니다.

모든 아키텍처에 일관되도록, include/linux/topology.h 는
include/asm-XXX/topoloty.h 에서 정의하지 않는 위들 매크로 중 무엇을 위한
기본 정의를 제공합니다:
1) physical_package_id: -1
2) core_id: 0
3) thread_siblings: 그냥 주어진 CPU
4) core_siblings: 그냥 주어진 CPU

Book(CONFIG_SCHED_BOOK)을 지원하지 않는 아키텍처에는 topology_book_id() 와
topology_book_cpumask() 의 기본 정의가 없습니다.
추가적으로, CPU 토폴로지 정보는 /sys/devices/system/cpu 아래에서 제공되고,
이들 파일을 포함합니다. 출력의 내부 소스는 브라켓들(“[]”) 안에 있습니다.

    kernel_max: 커널 설정[NR_CPUS-1]에서 허용되는 최고 CPU 인덱스

    offline:    핫플러그 오프(HOTPLUGGED off:cpu-hotplug.txt 참고)되었거나,
                커널 설정(위의 kernel_max)에서 허용되는 CPU들의 제한을
                초과핬기 때문에 온라인이 아닌 CPU들.
                [~cpu_online_mask + cpus >= NR_CPUS]

    online:     온라인이고 스케줄 중인 CPU들 [cpu_online_mask]

    possible:   자원을 할당받았고, 지금 존재하면 온라인이 될 수 있는
                CPU들. [cpu_possible_mask]

    present:    시스템에 현재 존재하고 있는 것으로 식별된 CPU들.
                [cpu_present_mask]

위 출력의 형식은 cpulist_parse()[<linux/cpumask.h>]와 호환됩니다.
아래 예제가 있습니다.

이 예제에서, 64개의 CPU들이 시스템 내부에 있지만, CPU 32-63은 32로
된 NR_CPUS 설정 옵션에 의해 0..31 로 제한된 커널 최고값을 초과했습니다.
CPU 2와 4-31들은 온라인이 아니지만, 존재(present)하고, 가능(possbile)하므로
온라인이 될 수 있습니다.

     kernel_max: 31
        offline: 2,4-31,32-63
         online: 0-1,3
       possible: 0-31
        present: 0-31

이 예제에서, NR_CPUS 설정 옵션은 128이지만, 커널은 possible_cpus=144로
시작되었습니다. 시스템 내에 4개의 CPU가 있고, cpu2는 수동으로 오프라인이
되었습니다(그리고, 그 하나의 CPU만 온라인이 될 수 있습니다).

     kernel_max: 127
        offline: 2,4-127,128-143
         online: 0-1,3
       possible: 0-127
        present: 0-3

possible_cpus=숫자 시작 파라미터는 다양한 cpumask 의 더 많은 정보로
좋은 cpu-hotplug.txt 를 보세요.

[Linux:Kernel] 오픈 펌웨어(Open Firmware) 없이 리눅스/ppc 커널 부팅하기

이 문서의 저작권은 GPL 라이센스를 따릅니다(This document is released under the GPL License).

Documentation/devicetree/booting-without-of.txt

        오픈 펌웨어(Open Firmware) 없이 리눅스/ppc 커널 부팅하기
        ——————————————————–

(c) 2005 Benjamin Herrenschmidt <benh at kernel.crashing.org>,
    IBM Corp.
(c) 2005 Becky Bruce <becky.bruce at freescale.com>,
    Freescale Semiconductor, FSL SOC and 32-bit additions
(c) 2006 MontaVista Software, Inc.
    Flash chip node definition

번역: 양정석<dasomoli@REMOVETHISgmail.com>

차례
====

  I – 도입
    1) arch/arm의 진입 포인트(Entry point)
    2) arch/powerpc의 진입 포인트
    3) arch/x86의 진입 포인트

  II – DT 블록 형식
    1) 헤더
    2) 디바이스 트리 일반론
    3) 디바이스 트리 “구조” 블록
    4) 디바이스 트리 “문자열들” 블록

  III – 디바이스 트리에서 필요한 내용
    1) 셀(cells)과 주소 표현에 관한 기록
    2) “compatible” 프로퍼티에 대한 기록
    3) “name” 프로퍼티에 대한 기록
    4) 노드와 프로퍼티 이름과 문자셋에 대한 기록
    5) 필요 노드와 프로퍼티들
      a) 루트 노드
      b) /cpus 노드
      c) /cpus/* 노드들
      d) /memory 노드(들)
      e) /chosen 노드
      f) /soc<SOC이름> 노드

  IV – “dtc”, 디바이스 트리 컴파일러(device tree compiler)

  V – 부트로더에 대한 권장 사항

  VI – 시스템-온-칩 디바이스와 노드들
    1) SOC의 자식 노드들 정의하기
    2) 현재의 OF 명세없이 디바이스들 나타내기

  VII – 디바이스들의 인터럽트 정보 지정하기
    1) interrupt 프로퍼티
    2) interrupt-parent 프로퍼티
    3) OpenPIC 인터럽트 컨트롤러
    4) ISA 인터럽트 컨트롤러

  VIII – 디바이스 전원 관리 정보 지정하기 (sleep 프로퍼티)

  부록 A – MPC8540의 예제 SOC 노드


개정 정보
=========

   May 18, 2005: Rev 0.1 – Initial draft, no chapter III yet.

   May 19, 2005: Rev 0.2 – Add chapter III and bits & pieces here or
                           clarifies the fact that a lot of things are
                           optional, the kernel only requires a very
                           small device tree, though it is encouraged
                           to provide an as complete one as possible.

   May 24, 2005: Rev 0.3 – Precise that DT block has to be in RAM
                         – Misc fixes
                         – Define version 3 and new format version 16
                           for the DT block (version 16 needs kernel
                           patches, will be fwd separately).
                           String block now has a size, and full path
                           is replaced by unit name for more
                           compactness.
                           linux,phandle is made optional, only nodes
                           that are referenced by other nodes need it.
                           “name” property is now automatically
                           deduced from the unit name

   June 1, 2005: Rev 0.4 – Correct confusion between OF_DT_END and
                           OF_DT_END_NODE in structure definition.
                         – Change version 16 format to always align
                           property data to 4 bytes. Since tokens are
                           already aligned, that means no specific
                           required alignment between property size
                           and property data. The old style variable
                           alignment would make it impossible to do
                           “simple” insertion of properties using
                           memmove (thanks Milton for
                           noticing). Updated kernel patch as well
                         – Correct a few more alignment constraints
                         – Add a chapter about the device-tree
                           compiler and the textural representation of
                           the tree that can be “compiled” by dtc.

   November 21, 2005: Rev 0.5
                         – Additions/generalizations for 32-bit
                         – Changed to reflect the new arch/powerpc
                           structure
                         – Added chapter VI


 ToDo:
        – Add some definitions of interrupt tree (simple/complex)
        – Add some definitions for PCI host bridges
        – Add some common address format examples
        – Add definitions for standard properties and “compatible”
          names for cells that are not already defined by the existing
          OF spec.
        – Compare FSL SOC use of PCI to standard and make sure no new
          node definition required.
        – Add more information about node definitions for SOC devices
          that currently have no standard, like the FSL CPM.


I – 도입
========

리눅스/ppc64 커널, 그리고 더 특정지어서, 오래된 IBM p시리즈/i시리즈
쌍 밖의 새로운 플랫폼 타입의 추가 개발 동안, 커널 진입과 부트 로더
<-> 커널 인터페이스에 관련된 엄격한 몇가지 규칙이 시행되도록 결정되었고,
ppc32 커널 진입 포인트와 그 방법이 되었던 것 같은 퇴보를 피하기 위해서,
새 플랫폼이 커널에 추가되어야 했습니다. 기존의 i시리즈 플랫폼은 이
계획(scheme)에 앞서 있어서 이들 규칙을 깹니다만, 그들을 적절히 따르지
않는 새로운 보드 지원은 하나도 메인 트리에 받아들여지지 않을 것입니다.
게다가 ppc32와 ppc64의 arch/powerpc로의 플랫폼 통합의 완료 이후에는,
새로운 32비트 플랫폼과 arch/powerpc 내로 옮기는 32비트 플랫폼은 이 룰을
잘 따르도록 요구될 것입니다.

아래에서 더 자세히 정의될 주된 요구사항은 오픈 펌웨어 명세(Open Firmware
specification) 이후에 정의된 형식의 디바이스 트리가 있느냐 하는 것입니다.
그러나 임베디드 보드 업체들의 삶이 더 쉽게 만들기 위해서, 커널은
시스템 내의 모든 디바이스를 표현한 디바이스 트리를 요구하지는 않고,
실제로 있는 노드와 항목들만 요구합니다. 이는 섹션 III에서 더 자세히
설명될 겁니다만, 예를 들면, 커널은 여러분에게 시스템 내의 모든 PCI
디바이스를 위한 노드를 만들 것을 요구하지 않습니다. 인터럽트 경로 정보와
다른 것 가운데에서 메모리/IO 범위를 제공하기 위해 PCI 호스트 브리지를
위한 노드 하나를 가지는 것이 요구 사항입니다. 또한 온칩 디바이스와
특별히 맞춰지지 않는 다른 버스들에 대한 노드들을 존재하는 OF 명세 안에
정의하는 것을 권장합니다. 이것은 커널이 모든 종류의 하드 코딩된 테이블
없이, 그들을 감지(probe)해서 드라이버를 디바이스와 맞출 수 있는 방법으로
대단한 유연성을 제공합니다. 또한 보드 업체들에게 커널 코드에 큰 영향없이,
또는 특정 경우로 집어넣는 일 없이 마이너 하드웨어 업그레이드를 하도록
더 유연하게 만듭니다.

1) arch/arm의 진입 포인트(Entry point)
————————————–

   커널 이미지의 시작점에 커널로의 하나의 진입 포인트가 있습니다. 그
   진입 포인트는 두가지 호출 규약을 지원합니다. 그 인터페이스의 요약이
   여기 기술되어 있습니다. 부팅 요구사항의 전체 설명은
   Documentation/arm/Booting 안에 문서화되어 있습니다.

        a) ATAGS 인터페이스. 펌웨어로부터의 최소한의 정보가 커널로
        미리 정의된(predefined) 파라메터의 태그를 붙인 리스트(tagged list)
        로 넘겨집니다.

                r0 : 0

                r1 : 머신 타입 넘버(Machine type number)

                r2 : 시스템 RAM 안의 태그를 붙인 리스트의 물리 주소

        b) 평면(flattened) 디바이스 트리 블록으로 진입. 펌웨어는 r2 안에서
        평면 디바이스 트리 블록(dtb)의 물리 주소를 로딩하고, r1 은 사용되지
        않습니다만, Documentation/arm/Booting 내에 설명된 것처럼 유효한 머신
        넘버를 사용하는 좋은 관례도 고려됩니다.

                r0 : 0

                r1 : 유효한 머신 타입 넘버. 디바이스 트리를 사용할 때,
                하나의 머신 타입 넘버가 SoC의 계열 또는 클래스를 나타내는데
                할당되곤 할 것입니다.

                r2 : (챕터 II에 정의된) RAM 안의 디바이스 트리 블록으로의
                물리적 포인터(physical pointer). 디바이스트리는 시스템 RAM 안
                어느 곳에나 위치할 수 있습니다만, 64비트 경계(boundary) 안에
                정렬되어야 합니다.

   커널은 ATGAS와 디바이스트리 부팅을 r2에 의해 가리켜지는 메모리를 읽고,
   평면 디바이스 트리 블록의 매직 값(0xd00dfeed) 또는 r2 에서 0x4 오프셋
   위치의 ATAG_CORE 값(0x54410001)을 살펴봄으로써 구별할 것입니다.

2) arch/powerpc의 진입 포인트
—————————–

   커널 이미지의 시작에 커널로의 하나의 진입 포인트가 있습니다. 그 진입
   포인트는 두가지 호출 규약을 지원합니다:

        a) 오픈 펌웨어로 부팅. 여러분의 펌웨어가 오픈 펌웨어(IEEE 1275)와
        호환되거나, OF 호환 클라이언트 인터페이스 API(나온 말들이 필요없는
        “interpret” 콜백이 지원되는)를 제공하면, 다음으로 커널로 진압할 수
        있습니다:

              r5 : IEEE 1275 powerpc로의 바인딩에 의해 정의되는 OF 콜백
              포인터. 32비트 클라이언트 인터페이스만 현재 지원됨.

              r3, r4 : 있거나, 또는 0이면, initrd의 주소와 길이

              MMU는 켜져 있거나 꺼져 있을 수 있습니다; 커널은
              arch/powerpc/kernel/prom_init.c 안에 위치한 분기 코드를
              오픈 펌웨어로부터 디바이스-트리와 다른 정보를 추출하고,
              평명화 디바이스-트리를 b에 서술한대로 만들기 위해 실행할
              것입니다). prom_init()은 그 후, 두번째 방법으로 커널을
              재진입할 것입니다. 이 분기 코드는 그 시점에 모든 예외를
              처리하는 펌웨어 컨텍스트에서 실행됩니다.

        b) 평면화 디바이스-트리 블록으로 직접 진입. 이 진입 포인트는 a)에
        의해 OF 분기 코드 후에 호출되고, 오픈 펌웨어 클라이언트 인터페이스를
        지원하지 않는 부트로더에 의해 직접 호출될 수 있습니다. 또한,
        “kexec”에 의해 이전에 실행되던 것에서부터 새로운 커널의 “핫” 부팅을
        구현하는데 사용될 수 있습니다. 이 방법은 방법 a) 가 간단히
        표준 오픈 펌웨어로서, 그래서 그를 정의하고 PowerPC 플랫폼으로의
        그 바인딩을 정의하는 다양한 표준 문서들에 따라 구현되어야 하는,
        제가 이 문서 내에서 더 자세히 설명할 방법입니다. 그래서, 그 진입
        포인트 정의는 다음처럼 됩니다:

                r3 : 램(RAM) 내의 (챕터 II에 정의된)디바이스-트리로의
                물리적 포인터

                r4 : 커널 그 자체로의 물리적 포인터. 이것은 어셈블리 코드에
                의해 여러분이 MMU를 켜거 1:1 맵핑이 아닌 상태로 커널로
                진입하는 경우에 적절히 MMU 를 끄기 위해 사용됩니다.

                r5 : NULL (방법 a와 구별하기 위함)

        SMP 진입에 관한 알림: 여러분의 펌웨어는 여러분의 다른 CPU 들을
        여러분이 신경쓸 필요가 없는 경우에 소프트 리셋이나 어떤 다른
        수단을 통해 수행할 수 있는 롬(ROM) 내의 어떤 슬립 루프나 스핀 루프에
        두거나, 모든 CPU로 커널 진입을 해야만 할 것입니다. 방법 b)로
        하는 방법은 이 문서의 이후 개정판에서 설명될 것입니다.

   보드 지원들(플랫폼들)은 설정 옵션들이 전용이 아닙니다. 임의의
   보드 지원들은 하나의 커널 이미지로 빌드될 수 있습니다. 커널은
   디바이스-트리의 내용에 기초하여 주어진 플랫폼에서 사용될 함수 집합을
   “알” 것입니다. 그래서 여러분은 다음처럼 해야만 합니다:

        a) arch/powerpc/Kconfig 내에 _불린(boolean)_ 옵션으로,
        PPC_PSERIES, PPC_PMAC 과 PPC_MAPLE의 예제를 따라, 여러분의
        플랫폼 지원을 추가하세요. 나중 것이 아마 시작하기 좋은 보드 지원
        예제가 될 것입니다.

        b) 여러분의 주 플룟폼 파일을
        “arch/powerpc/platforms/myplatform/myboard_setup.c” 로 생성하고
        여러분의 CONFIG_ 옵션의 조건 아래서 Makefile 에 추가하세요.
        이 파일은 일반적인 코드가 여러분의 플랫폼에 따른 코드를 얻어 사용할
        다양한 콜백을 포함하는 “ppc_md” 타입의 구조체를 정의할 것입니다.

  플랫폼 기능이 같은 코어 아키텍처를 사용할 때이긴 하지만, 하나의 커널
  이미지가 여러 플랫폼을 지원할 것입니다. 하나의 커널 빌드는 책 E 와
  고전적인 Powerpc 아키텍처의 설정 둘 다를 지원할 수는 없습니다.

3) arch/x86의 진입 포인트
————————-

  code32_start에 커널로의 하나의 32비트 진입 포인트, 압축 해제기
  (리얼 모드 진입 포인트는 보호 모드 안으로 전환되면 같은 32비트 진입
  포인트로 갑니다)가 있습니다. 그 진입 포인트는 Documentation/x86/boot.txt
  안에 문서화된 하나의 호출 규약을 지원합니다.
  (챕터 II 안에 정의된) 디바이스 트리 블록으로의 물리적 포인터는 적어도
  부트 프로토콜 2.09를 요구하는 setup_data 를 통해 넘겨집니다. 그 타입
  (type) 필드는 다음처럼 정의됩니다.

  #define SETUP_DTB                      2

  이 디바이스 트리는 “부트 페이지(boot page)”로의 확장처럼 사용됩니다.
  그처럼 부트 페이지에 의해 이미 대신되는 데이터를 파싱 / 고려하지 않습니다.
  이것은 메모리 크기, 예약된 범위(reserved ranges), 커맨드 라인 인자,
  또는 initrd 주소를 포함합니다. 검색할 수 없는 정보, 아니면 인터럽트 경로나
  I2C 버스 뒤의 디바이스 리스트 같은 정보를 간단히 보유합니다.

II – DT 블록 형식
=================


이 챕터는 커널로 넘기는 평면 디바이스 트리의 실제 형식을 정의합니다.
그 실제 내용과 커널 요구사항은 이후에 설명됩니다. 여러분은 오픈 펌웨어
상세(representation)로부터 생성될 평면 디바이스 트리인
arch/powerpc/kernel/prom_init.c 또는 파일 시스템 상세로부터 생성될
kexec 툴의 일부인 fs2dt 유틸리티를 포함하여 다양한 곳의 형식을 조작하는
코드 예제를 찾을 수 있습니다. uboot 같은 부트로더는 이후에 좀 더 이야기할
조금 더 많은 지원이 기대됩니다.


주의: 그 블록은 주 메모리 안에 있어야 합니다. 주 메모리 외에 다른 매핑없이
리얼 모드와 가상 모드 모두 에서 접근 가능해야 합니다. 간단한 플래시
부트로더를 작성하는 중이라면, 그 블록을 커널로 넘기기 전에 RAM으로 복사해야
합니다.


1) 헤더
——-

   커널은 include/linux/of_fdt.h 안에 boot_param_header 구조체로 대강 설명된
   한 메모리 영역을 가리키는 물리 주소를 넘겨 받습니다:

struct boot_param_header {
        u32     magic;                  /* 매직 워드 OF_DT_HEADER */
        u32     totalsize;              /* DT block의 전체 크기 */
        u32     off_dt_struct;          /* 구조의 오프셋 */
        u32     off_dt_strings;         /* 문자열들의 오프셋 */
        u32     off_mem_rsvmap;         /* 메모리 예약맵의 오프셋 */
        u32     version;                /* 형식 버전 */
        u32     last_comp_version;      /* 마지막 호환되는 버전 */

        /* 아래는 버전 2 필드 */
        u32     boot_cpuid_phys;        /* 우리가 부팅하는 물리 CPU id */
        /* 아래는 버전 3 필드 */
        u32     size_dt_strings;        /* 문자열들 블록의 크기 */

        /* 아래는 버전 17 필드 */
        u32     size_dt_struct;         /* DT 구조 블록의 크기 */
};

   상수를 덧붙입니다:

/* 평면 디바이스 트리에 의해 사용되는 정의들 */
#define OF_DT_HEADER            0xd00dfeed      /* 4: 버전,
                                                   4: 전체 크기 */
#define OF_DT_BEGIN_NODE        0x1             /* 시작 노드: 전체 이름
                                                   */
#define OF_DT_END_NODE          0x2             /* 끝 노드 */
#define OF_DT_PROP              0x3             /* 프로퍼티: name off,
                                                   크기, 내용 */
#define OF_DT_END               0x9

   이 헤더 안의 모든 값은 빅 엔디안(big endian) 형식이고, 다양한 필드들이
   아래에 더 정밀하게 정듸됩니다. 모든 “오프셋(offset)” 값들은 헤더의
   시작으로부터의 바이트 단위 값입니다: 디바이스 트리 블록의 물리적
   베이스 주소입니다.

   – magic

     디바이스 트리 블록 헤더의 시작을 “표시하는” 매직 값입니다.
     0xd00feed 값을 포함하고, OF_DT_HEADER 상수로 정의됩니다.

   – totalsize

     이것은 헤더를 포함한 DT 블록의 전체 크기입니다. 그 “DT” 블록은
     (이 헤더 안의 오프셋들에 의해서 가리켜지는)이 챕터에서 정의하는
     모든 데이터 구조체를 갖고 있어야 합니다. 디바이스 트리 구조, 문자열들,
     메모리 예약 맵을 말합니다.

   – off_dt_struct

     이것은 헤더의 시작으로부터 디바이스 트리 “구조” 부분의 시작까지의
     오프셋입니다. (2) 디바이스 트리 를 보세요)

   – off_dt_strings

     이것은 헤더의 시작으로부터 디바이스 트리 “문자열들” 부분의
     시작까지의 오프셋입니다.

   – off_mem_rsvmap

     이것은 헤더의 시작으로부터 예약 메모리 맵의 시작까지의 오프셋입니다.
     이 맵은 64비트 정수 쌍의 리스트입니다. 각 쌍은 물리 주소와
     크기입니다. 그 리스트는 크기 0의 항목으로 끝납니다. 이 맵은
     커널에게 “예약된” 물리 메모리 영역의 리스트를 제공하고, 그래서
     특히 초반 초기화 시에, 메모리 할당에 사용되지 않습니다. 커널은
     부팅하는 동안 디바이스 트리의 평면 해제화, MMU 해시 테이블, 기타…
     등을 위한 메모리를 할당할 필요가 있습니다. 이 할당은 오픈 펌웨어
     머신 상에서의 RTAS 인스턴스나, 특정 p시리즈 상에서 iommu 에서
     사용되는 TCE 테이블 같은 치명적인 것들을 덮어씌우는 것을 피하도록
     앞의 방법으로 수행되어야만 합니다. 일반적으로, 예약 맵은 _적어도_
     이 DT 블록 자체(header, total_size)는 포함해야 합니다. 여러분이
     커널로 initrd를 넘기고 있다면, 잘 예약해야 합니다. 커널 이미지
     자체를 예약할 필요는 없습니다. 그 맵은 64 비트로 정렬되어 있어야
     합니다.
     

   – version

     이것은 이 구조체의 버전입니다. 버전 1은 여기서 멉춥니다. 버전 2는
     추가적인 필드 boot_cpuid_phys를 추가합니다. 버전 3은 커널이 부팅
     시에 그를 쉽게 재할당하고 사용되지 않는 평면 구조를 확장 후에
     해제할 수 있도록, 문자열들 블록의 크기를 추가합니다. 버전 16은
     트리 자체의 새로운 좀 더 “작은”, 그러나 이전과 호환되지는 않는
     형식을 도입합니다. 버전 17은 재할당하거나, 더 쉽게 옮길 수 있도록
     할 수 있는 추가적인 필드, size_dt_struct 를 추가합니다(이는 감지된
     정보를 기초로 디바이스 트리로 조정하는 부트 로더에 특히 유용합니다).
     여러분은 언제나 여러분의 구현 시점에 정의된 가장 높은 버전의
     구조체를 만드는 것이 좋습니다. 여러분이 명시적으로 하위 호환성에
     초점을 맞추고 있지 않다면, 현재는 버전 17입니다.
     

   – last_comp_version

     마지막 호환 버전. 이것은 여러분이 하위 호환을 제공하는 DT 블록이
     무슨 버전인지를 나타냅니다. 예를 들면, 버전 2는 버전 1과 하위
     호환(즉, 버전 1의 커널 빌드는 버전 2 형식으로 부팅됨)됩니다.
     여러분이 버전 1에서 3의 디바이스 트리를 생성한다면, 이 필드에 1을,
     버전 16이나 새로운 유닛 이름 형식을 사용하는 17의 디바이스 트리를
     생성한다면 16을 넣는 것이 좋습니다.

   – boot_cpuid_phys

     이 필드는 버전 2 헤더에만 존재합니다. 그것은 어떤 CPU ID 가 커널
     진입점을 호출하는지를 나타냅니다. 이것은 다른 것들 중에서도 kexec에
     의해서 사용됩니다. 만약 여러분이 SMP 시스템 상에 있다면, 이 값은
     커널 진입점을 호출하는 CPU에 해당하는 디바이스 트리 안의 CPU 노드의
     “reg” 프로퍼티의 내용과 맞아야 합니다(요구되는 디바이스 트리 내용에
     대한 더 많은 정보는 다른 챕터를 보세요).

   – size_dt_strings

     이 필드는 버전 3과 그 이후 헤더에만 존재합니다. 그것은
     (off_dt_strings로 주어지는 오프셋에서 시작하는) 디바이스 트리의
     “문자열들” 섹션의 크기를 줍니다. 

   – size_dt_struct

     이 필드는 버전 17과 그 이후 헤더에만 존재합니다. 그것은
     (off_dt_struct로 주어지는 오프셋에서 시작하는) 디바이스 트리의
     “구조” 섹션의 크기를 줍니다.

   그래서 DT 블록의 전형적인 배치는 (여러 부분들이 저 순서일 필요는
   없지만) 다음과 같습니다(주소는 위에서 아래로 진행):


             ——————————
     base -> |  struct boot_param_header  |
             ——————————
             |   (정렬을 위한 공간) (*)   |
             ——————————
             |       메모리 예약 맵       |
             ——————————
             |     (정렬을 위한 공간)     |
             ——————————
             |                            |
             |     디바이스 트리 구조     |
             |                            |
             ——————————
             |     (정렬을 위한 공간)     |
             ——————————
             |                            |
             |   디바이스 트리 문자열들   |
             |                            |
      —–> ——————————
      |
      |
      — (base + totalsize)

  (*) 정렬을 위한 공간이 반드시 있지는 않습니다; 그 존재와 크기는 개개의
      데이터 블록의 다양한 정렬 요구 사항에 달려 있습니다.


2) 디바이스 트리 일반론
———————–

디바이스 트리 그 자체는 두가지 다른 블록, 구조 블록과 문자열들 블록으로
나뉘어 집니다. 둘 모두 4바이트 경계로 정렬될 필요가 있습니다.

먼저, 저장 형식을 상세히 하기 전에 디바이스 트리 개면에 대해 빠르게
설명해 봅시다. 이 챕터는 커널을 위한 노드와 프로퍼티들의 요구 타입의
자세한 사항을 설명하지는 _않습니다_. 이것은 챕터 III에서 나중에 합니다.

디바이스 트리 배치는 오픈 펌웨어 IEEE 1275 디바이스-트리 의 정의를
강력하게 상속받습니다. 그것은 기본적으로 각 노드가 두가지 혹은 그 이상의
이름있는 프로퍼티를 가지는 노드들의 하나의 트리입니다. 프로퍼티는
값을 가질 수도, 가지지 않을 수도 있습니다.

그것은 하나의 트리입니다. 그래서 부모가 없는 루트 노드를 제외한 각 노드는
오직 하나의 부모를 갖습니다.

하나의 노드는 2개의 이름을 갖습니다. 실제 노드 이름은 일반적으로 노드
프로퍼티 리스트 안의 값이 0으로 끝나는 문자열인 “이름(name)” 타입의
프로퍼티 안에 포함되고, 이는 (오픈 펌웨어 안에 있는 것처럼) 형식 정의의
버전 1에서 3까지에서는 필수 사항입니다. 버전 16은 아래 정의된 유닛
이름으로부터 이를 생성할 수 있기 때문에, 그것을 선택 사항으로
만들었습니다.

같은 레벨의 같은 이름과 노드를 구분하는데 사용되는 “유닛 이름(unit
name)”도 있는데, 그것은 보통 정의가 그 노드가 부착된 버스 타입으로
특정되는 노드 이름, “@” 기호과 “유닛 주소”로 만듭니다.

유닛 이름은 그 자체로 프로퍼티로 존재하지는 않지만, 디바이스 트리
구조 안에 포함됩니다. 그것은 일반적으로 디바이스 트리 안의 “경로
(path)”를 나타내는데 사용됩니다. 실제 이들 형식에 대한 더 자세한 사항은
아래에서 나타날 것입니다.

커널의 일반적인 코드는 유닛 주소의 어떠한 정규적인 용도를 (어떤 보드
지원 코드가 한다고 하더라도) 만들지 않기 때문에, 여기서의 유닛 주소를
위한 진짜 요구 사항은 그 트리의 주어진 레벨에서의 그 노드 유닛 이름의
유일성을 확인하는 것입니다. 주소 표현 없는 노드들과 (/memory 또는
/cpus 같은) 동일한 이름의 가능하지 않은 형제는 이 명세의 문맥 안에서
유닛 주소를 생략할 것이고, 또는 기본 유닛 주소 “@0″을 사용할 것입니다.
그 유닛 이름은 “/”로 분리된 모든 부모 노드 유닛 이름들을 이어 붙인 
노드 “전체 경로(full path)”를 정의하는데 사용됩니다.


루트 노드는 정의된 이름을 갖지 않고, 여러분이 버전 3 혹은 그 이전의
형식을 사용하고 있다면 name 프로퍼티를 갖도록 요구되지 않습니다. 또한,
유닛 주소도 갖지 않습니다(유닛 주소로 @ 기호가 뒤따르지 않음). 루트
노드 유닛 이름은 그래서 빈 문자열입니다. 루트 노드로의 전체 경로는
“/” 입니다.

실제 디바이스를 실제로 나타내는 (즉, “/cpus” 같은 다른 노드를 위한
가상 “컨테이너”일 뿐인 노드가 아닌) 각 노드는 또한 특정 하드웨어와
그것과 완전한 하위 호환되는 디바이스의 옵션 리스트를 나타내는
“compatible” 프로퍼티를 갖습니다.

마지막으로, 다른 노드 안의 프로퍼티로부터 참조될 수 있는 각 노드는
“phandle” 이나 “linux,phandle” 프로퍼티를 가져야 합니다. 진짜 오픈
펌웨어 구현은 “prom_init()” 점프 코드가 “linux,phandle” 프로퍼티로
바꾸는 각 노드의 유일한 “phandle” 값을 제공합니다. 그러나, 이것은
평면 디바이스 트리가 직접 사용된다면, 선택 사항이 됩니다. 다른 노드를
“phandle”을 통해 참조하는 노드의 예는 이 문서의 이후 버전 안에서
서술될 인터럽트 트리를 배치할 때 같은 것입니다.

“phandle” 프로퍼티는 노드를 유일하게 식별하는 32 비트 값입니다.
시스템 값, 내부 포인터같은 어떠한 값을 사용하거나, 이들을 어떻게
생성하든 맘대로입니다. 유일한 요구 사항은 각 노드가 그를 위한 유일한
값을 가지는 프로퍼티를 제공하는 것 뿐입니다.

여기 간단한 디바이스 트리 예제가 있습니다. 이 예제에서, “o” 는 노드
유닛 이름이 따름을 가리킵니다. 프로퍼티들은 그 내용이 따르는 그들의
이름으로 나타납니다. “내용”은 (0으로 끝나는) ASCII 문자열 값을
나타내는 반면, <내용>은 10진수나 (0x 접두사 뒤의) 16진수로 지정되는
32비트 값을 나타냅니다. 이 시점에서, 저는 여러분에게 실제로 트리가
무엇처럼 보이는지 더 좋은 생각을 제공하기 위해서 의도적으로 “name”과
“linux,phandle” 프로퍼티를 남겨두었습니다.

  / o device-tree
      |- name = “device-tree”
      |- model = “MyBoardName”
      |- compatible = “MyBoardFamilyName”
      |- #address-cells = <2>
      |- #size-cells = <2>
      |- linux,phandle = <0>
      |
      o cpus
      | | – name = “cpus”
      | | – linux,phandle = <1>
      | | – #address-cells = <1>
      | | – #size-cells = <0>
      | |
      | o PowerPC,970@0
      |   |- name = “PowerPC,970”
      |   |- device_type = “cpu”
      |   |- reg = <0>
      |   |- clock-frequency = <0x5f5e1000>
      |   |- 64-bit
      |   |- linux,phandle = <2>
      |
      o memory@0
      | |- name = “memory”
      | |- device_type = “memory”
      | |- reg = <0x00000000 0x00000000 0x00000000 0x20000000>
      | |- linux,phandle = <3>
      |
      o chosen
        |- name = “chosen”
        |- bootargs = “root=/dev/sda2”
        |- linux,phandle = <4>

이 트리는 거의 최소한의 트리입니다. 그것은 리눅스 커널을 부팅하기 위해
필요한 노드와 프로퍼티의 최소한만큼만 멋지게 포함합니다;
즉, 루트의 기본 모델 정보, CPU들, 그리고 물리 메모리 배치입니다.
또한 그것은 /chosen을 통해 넘어오는 부가적인 정보, 예를 들면, 플랫폼
타입(필수 사항)와 커널 커맨드 라인 인자(선택 사항)을 포함합니다.


/cpus/PowerPC,970@0/64-bit 프로퍼티는 값이 없는 프로퍼티의 한 예입니다.
모든 다른 프로퍼티는 하나의 값을 가집니다. #address-cless와 $size-cells
프로퍼티가 지정하는 것은 필요로 하는 노드와 프로퍼티, 그들의 내용을
정밀하게 정의하는 챕터 IV에서 설명될 것입니다.


3) 디바이스 트리 “구조” 블록

디바이스 트리의 구조는 선형 트리 구조입니다. “OF_DT_BEGIN_NODE” 토큰은
새 노드를 시작하고, “OF_DT_END_NODE”는 노드 정의를 끝냅니다. 자식
노드들은 (그 노드 안의 노드들) “OF_DT_END_NODE” 전에 간단히 정의됩니다.
‘토큰(token)’은 32비트 값입니다. 트리는 OF_DT_END 토큰으로 끝납니다.

여기 한 노드의 기본 구조입니다:

     * (0x00000001인) OF_DT_BEGIN_NODE 토큰
     * 버전 1에서 3은 이것이 0으로 끝나고, “/”로 시작하는 노드 전체
       경로입니다. 버전 16과 그 이후에 이것은 노드 유닛 이름(또는,
       루트 노드의 빈 문자열)일 뿐입니다.
     * [다음 4바이트 경계로 공간을 정렬]
     * 각 프로퍼티는:
        * (0x00000003인) OF_DT_PROP 토큰
        * 바이트로 된 프로퍼티 값 크기인 32비트 값(또는 값이 없으면 0)
        * 프로퍼티 이름의 문자열 블록 안의 오프셋인 32비트 값
        * 있다면, 프로퍼티 값 데이터
        * [다음 4바이트 경계로 공간을 정렬]
     * [있다면, 자식 노드들]
     * (0x00000002인) OF_DT_END_NODE 토큰

그래서 노드 내용은 시작 토큰, 전체 경로, 프로퍼티의 리스트, 자식 노드의
리스트, 그리고 끝 토큰으로 요약될 수 있습니다. 모든 각 자식 노드는 위에
정의된 것 같은 그 자체가 전체 노드 구조입니다.

알림: 위의 정의는 특정 노드를 위한 모든 프로퍼티 정의들이 그 노드를 위한
어떤 보조 노드 정의보다 우선해야 함을 필요로 합니다. 프로퍼티들과 보조
노드 들이 막 섞여 있다면, 그 구조가 애매하지 않아야 하지만, 커널 파서
(parser)는 그 프로퍼티들이 먼저 올 것을 (적어도 2.6.22까지는) 요구합니다.
어떤 (평면 드리를 조작하는) 툴도 이 제약을 지켜야 합니다.

4) 디바이스 트리 “문자열들” 블록

공간을 아끼기 위해서, 일반적으로 중복되는 프로퍼티 이름들을 “문자열들”
블록 안에 별도로 저장합니다. 이 블록은 간단히 모든 프로퍼티 이름을 위한
0으로 끝나는 문자열 전부를 연이어 붙여놓은 것입니다. 구조 플록 안의
디바이스 트리 프로퍼티 정의들은 문자열들 블록의 시작으로부터의 오프셋
값을 포함할 것입니다.


III – 디바이스 트리에서 필요한 내용
===================================

경고: 이 문서 내에 정의된 모든 “linux,*” 프로퍼티들은 평면 디바이스
트리에만 적용됩니다. 만약 여러분의 플랫폼이 오픈 펌웨어의 진짜 구현이나,
오픈 펌웨어 클라이언트 인터페이스와 호환되는 구현을 사용한다면, 그들
프로퍼티들은 커널의 prom_init() 파일 내의 점프 코드에 의해서 생성될
것입니다. 예를 들면, 여러분이 여러분의 보드 모델을 감지하기 위한 코드를
넣고, 플랫폼 넘버를 셋팅해야만 하는 곳 말입니다. 그러나, 평면 디바이스
트리 진입점을 사용할 때는 prom_init() 을 지나지 않고, 그래서 여러분은
그들 프로퍼티를 스스로 제공해야만 합니다.


1) 셀(cells)과 주소 표현에 관한 기록
————————————

일반적인 룰은 다양한 오픈 펌웨어 문서들 내에 문서화되어 있습니다.
여러분이 버스를 디바이스 트리 서술하는 것을 고르고, OF 버스 바인딩이
있다면, 그 명세를 따르는 것이 좋습니다. 그러나 커널은 디바이스 트리에
의해서 설명되는 각각의 모든 독립된 디바이스나 버스를 요구하지 않습니다.

일반적으로 디바이스를 위한 주소의 형식은 부모 버스 타입에 의해
#address-cells 와 #size-cells 프로퍼티에 기초하여 정의됩니다.
그 부모의 부모 #address-celss와 #size-cells 정의들은 상속되지 않으므로
자실을 가진 모든 각각의 노드는 그들을 지정해야 합니다. 커널은 루트 노드가
직접 프로세스 버스 상에 맵핑된 디바이스들을 위한 주소들의 형식을 정의하는
이들 프로퍼티를 가질 것을 요구합니다.

이들 2 프로퍼티는 주소와 크기를 표현하기 위한 ‘cells’를 정의합니다.
예를 들면, 만약 둘 다 위에서 주어졌던 예제 트리 같이 2를 갖는다면,
주소와 크기는 모두 2 셀로 구성되고, 각각은 64비트 수(셀들은 이어 붙고,
빅 엔디안 형식일 것으로 여겨집니다)입니다. 또다른 예제는 애플 펌웨어가
그들을 하나의 주소를 위한 두 셀과 크기를 위한 하나의 셀로 정의하는
방법입니다. 대부분의 32비트 구현들은 #address-cless와 #size-cless를
32비트 값으로 표현하는 1로 정의해야 합니다. 어떤 32비트 프로세서들은
32비트 이상의 물리 주소를 혀용합니다; 이들 프로세서들은 #address-celss를
2로 정의해야 합니다.

“reg” 프로퍼티들은 언제나 버스 #address-cless와 #size-cells에 의해 
정해지는 주소와 크기의 셀의 개수인 “address size” 타입의 튜플입니다.
버스는 여러가지 주소 공간과 조어진 주소 할당(prefetchable이나 기타 등등
같은)과 연관된 다른 플래그들을 지원합니다. 이들 플래그들은 일반적으로
물리 주소의 최상위 비트에 붙습니다. 예를 들면, PCI 물리 주소는 3 셀로
만들어지는데, 가장 아래 둘이 실제 주소 자체를 포함하는 반면, 최상위
셀이 주소 공간 표현, 플래그들, 그리고 pci 버스 & 디바이스 수를 가집니다.

동적 할당 지원을 위한 버스를 위해, “reg” 안의 주소를 제공하지 않기
(0을 갖음)는 하지만, 주소가 동적으로 할당되는 것을 표시하는 플래그를
제공하고, 완전히 할당된 주소들을 갖는 개별 “assigned-addresses”
프로퍼티를 제공하는 것이 관행입니다. 자세한 사항은 PCI OF 바인딩을
보세요.

보통, 주소 공간 비트가 없고, 동적 할당도 없는 간단한 버스가 여러분의
하드웨어를 반영한다면, 존재하는 커널 주소 파싱 함수들이 틀 밖에서
실행되므로 더 좋습니다. 만약 여러분이 주소 공간 비트 같은 것들을 포함하는
더 복잡한 주소 형식으로 버스 타입을 정의한다면, 버스 번역기를 여러분의
버스 타입을 위한 최신 커널의 prom_parse.c 파일에 덧붙여야만 할 것입니다.


“reg” 프로퍼티는 조어진 버스 내의 오직 주소와 크기만 (#size-cells가 0이
아니라면) 정의합니다. 위로 주소를 바꾸기 위해서(부모 버스 주소로,
가능하면 CPU 물리 주소로), 모든 버스는 “ranges” 프로퍼티를 포함해야만
합니다. “ranges” 프로퍼티가 주어진 레벨에 없다면, 주소 변환은 불가능한
것으로, 즉, 부모 버스 상에 레지스터들이 안보이는 것으로 가정합니다.
버스를 위한 “ranges” 프로퍼티의 형식은 다음의 리스트입니다:

        버스 주소, 부모 버스 주소, 크기

“버스 주소”는 이 버스 노드가 정의하는 버스의 형식 안에 있습니다. 즉,
PCI 브릿지에서는 PCI 주소가 될 겁니다. 그래서 (버스 주소, 크기) 는
자식 디바이스의 주소의 범위를 정의합니다. “부모 버스 주소”는 이 버스의
부모 버스의 형식 안에 있습니다. 예를 들면, PCI 호스트 컨트롤러에서는
CPU 주소가 될 것입니다. PCI<->ISA 브릿지에서는 PCI 주소가 될 겁니다.
이것은 그 범위의 시작이 맵핑된 부모 버스 내의 베이스 주소를 정의합니다.

64비트 보드 지원을 위해서, 저는 2/2 형식이나 크기가 보통 하나의 32비트
워드 안에 들어가기 때문에 약간 더 작은 애플의 2/1 형식을 추천합니다.
새로운 32비트 보드 지원은 그 프로세서가 2/1 형식이 권장되는 32비트
이상의 물리 주소를 지원하기 전까지는 1/1 형식을 사용해야 합니다. 

아니면, 그 레지스터들이 식별 맵핑 변환을 사용하는 부모 버스 상에서 보임을
나타내는 것으로, “ranges” 프로퍼티를 빈 상태로 둡니다. 다른 말로 하면,
그 부모 버스 주소 공간은 자식 버스 주소 공간과 같다는 것입니다.

2) “compatible” 프로퍼티에 대한 기록
————————————

이들 프로퍼티들은 선택 사항입니다만, 디바이스와 루트 노드 안에서
권장됩니다. “compatible” 프로퍼티의 형식은 이어진 0으로 끝나는 문자열의
하나의 리스트입니다. 그들은 디바이스가 그들의 유사한 디바이스 계열과의
호환성을, 어떤 경우에는 하나의 드라이버가 그들의 실제 이름과 상관없이
여러 디바이스들과 맞춰지는 것을 나타낼 수 있습니다. 

3) “name” 프로퍼티에 대한 기록
——————————

OldWorld 매킨토시 같은 오픈 펌웨어의 초기 사용자들은 “name” 프로퍼티에
실제 디바이스 이름을 사용하는 경향이 있었던 반면, 오늘날에는 디바이스
클래스(종종 device_type과 같은)에 더 가까운 이름을 사용하는 것이
좋은 실제 예제로 고려됩니다. 예를 들면, 오늘날에는, 이더넷 컨트롤러는
“ethernet”으로, 그 칩 타입/모델을 정확히 정의하는 추가적인 “model”
프로퍼티, 하나의 드라이버가 하나 이상의 이들 칩을 드라이버할 수 있는
경우 그 계열을 정의하는 “compatible” 프로퍼티로 이름 지어집니다.
그러나, 커널은 보통 “name” 프로퍼티 상에 어떤 제한도 두지 않습니다;
그것은 간단히 표준을 따르고, 그 진화에 가능한한 가까이하고자 하는
좋은 관례입니다.


새로운 형식 버전 16은 “name” 프로퍼티를 선택 사항으로 만들었음을 또한,
알아두세요. 어느 노드에 그게 없다면, 그 노드의 유닛 이름이 그 이름을
재구성하는데 사용됩니다. 즉, “@” 기호 전의 그 유닛 이름의 일부가
사용됩니다(또는 “@” 기호가 없다면, 전체 유닛 이름).

4) 노드와 프로퍼티 이름과 문자셋에 대한 기록
——————————————–

오픈 펌웨어가 8859-1의 더 유연한 용법을 제공하는 반면, 이 명세는
더 제한적인 규칙을 강제합니다. 노드와 프로퍼티들은 ASCII 문자들,
‘a’에서 ‘z’, ‘0’에서 ‘9’, ‘,’, ‘.’, ‘_’, ‘+’, ‘#’, ‘?’, 그리고 ‘-‘로만
조합되어야 합니다. 노드 이름은 추가적으로 대문자들 ‘A’에서 ‘Z’
(프로퍼티 이름은 소문자여야 합니다. 애플과 같은 업체들은 이 규칙을
존중하지 않는다는 사실은 여기서 무관합니다)를 허용합니다. 추가적으로,
노드와 프로퍼티 이름들은 언제나 ‘a’에서 ‘z'(또는 노드 이름에 ‘A’에서
‘Z’) 안의 문자로 시작해야 합니다.

노드와 프로퍼티 이름 모두, 문자의 최대 개수는 31입니다. 노드 이름의
경우, 이것은 유닛 이름의 맨 왼쪽의 이름(순수한 “name” 프로퍼티)이고,
그 제한 너머로 확장될 수 있는 유닛 주소를 포함하지 않습니다.

5) 필요 노드와 프로퍼티들
————————-
  이들이 현재 필요한 모두입니다. 그러나, 오픈 펌웨어로 PCI 바인딩 안에
  PCI 호스트 브릿지를 문서화하고, 여러분의 인터럽트 트리를 OF 인터럽트
  트리 명세 안에 문서화하여 노출하는 것을 강력히 권장합니다.
  

  a) 루트 노드

  루트 노드는 존재하기 위해서 몇가지 프로퍼티가 필요합니다:

    – model : 이것은 여러분의 보드 이름/모델입니다.
    – #address-cells : “root” 디바이스의 주소 표현
    – #size-cells: “root” 디바이스의 크기 표현
    – compatible : 여기서 그들의 방법으로 보통 찾을 수 있는 보드 “계열”,
      예를 들면, 커널 내의 같은 플랫폼 코드로 보통 구동될 수 있는
      유사한 배치의 2 보드 모델이 있다면, 여러분은 정확한 보드 모델을
      SOC 모델을 나타내는 항목에 따른 compatible 프로퍼티 내에 지정할
      것 입니다.

  루트 노드는 또한 아무거나 일련 번호같은 여러분의 보드에 따른 추가적인
  프로퍼티를 추가하는 곳 입니다. 표준이 정의하는 어떤 것과 충돌할만한
  어떤 “맞춤형” 프로퍼티가 있다면, 벤터 이름과 콤마를 앞에 붙이는 것이
  권장됩니다.

  b) /cpus 노드

  이 노드는 모든 개별 CPU 노드들의 부모입니다. 어떤 지정 요구 사항도
  없긴하지만 일반적으로 적어도 다음을 가지는 것이 좋은 관례입니다:

               #address-cells = <00000001>
               #size-cells    = <00000000>

  이것은 한 CPU의 “주소”가 하나의 셀이고, 의미없는 크기를 가지고
  있음을 정의합니다. 이것이 필요하진 않지만, 커널은 한 CPU 노드의 “reg”
  프로퍼티를 읽을 때의 형식을 추정하고 아래를 볼 것입니다.

  c) /cpus/* 노드들

  그래서 /cpus 아래에서, 여러분은 머신 상의 모든 개별 CPU의 노드를
  생성한다고 추측할 수 있습니다. CPU 이름에 어떤 지정 제한은 없지만,
  <아키텍처>,<코어> 로 부르는 게 일반적입니다. 예를 들면, 애플은
  PowerPC,G5를 사용하는 반면, IBM 은 PowerPC,970FX를 사용합니다.
  그러나 일반적인 이름 관례는 모든 개별 cpu 노드에 간단히 ‘cpu’를
  사용하고, 특정 cpu 코어를 식별하기 위해서 compatible 프로퍼티를
  사용하는 것이 더 좋다고 합니다.

  필수 프로퍼티:

    – device_type : “cpu”가 되어야만 함.
    – reg : 이것은 물리적 CPU 넘버입니다. 하나의 32비트 셀이고, 또한,
      전체 경로 안의 유닛 이름을 재구성하기 위한 유닛 넘버로써 그냥
      사용됩니다. 예를 들면, 2 CPU에서 전체 경로는 다음과 같습니다:
        /cpus/PowerPC,970FX@0
        /cpus/PowerPC,970FX@1
      (유닛 주소는 0으로 시작할 필요는 없습니다)
    – d-cache-block-size : 하나의 셀, 바이트로 된 L1 데이터 캐시 블록
      크기 (*)
    – i-cache-block-size : 하나의 셀, 바이트로 된 L1 명령 캐시 블록 크기
    – d-cache-size : 하나의 셀, 바이트로 된 L1 데이터 캐시 크기
    – i-cache-size : 하나의 셀, 바이트로 된 L1 명령 캐시 크기

(*) 캐시 “블록” 크기는 캐시 관리 명령 동작 상의 크기입니다. 역사적으로,
이 문서는 여기서 올바르지 않은 캐시 “라인” 크기를 사용합니다. 커널은
캐시 블록 크기를 더 선호할 것이고, 하위 호환성을 위해 캐시 라인 크기로
후퇴할 것입니다.

  권장 프로퍼티:

    – timebase-frequency : Hz로 된 시간 축의 주파수를 나타내는 셀. 이것은
      일반적인 코드에 의해 직접 사용되지는 않지만, 이 값을 기초로 커널
      시간 축/감쇠기 보정을 셋팅하기 위해 p시리즈 코드를 복사/붙여넣기하는
      것도 괜찮습니다.
    – clock-frequency : Hz로 된 CPU 코어 클럭 주파수를 나타내는 셀.
      새로운 프로퍼티는 64비트 값으로 정의될 것이지만, 여러분의 주파수가
      < 4GHz 이면, 하나의 셀로 충분합니다. 여기서는 위에서처럼 좋게,
      일반적인 코드는 이 프로퍼티를 사용하지 않습니다만, p시리즈나
      Maple 것을 재사용하는 것도 괜찮습니다. 미래의 커널 버전은 이를 위한
      일반적인 코드를 제공할 것입니다.
    – d-cache-line-size : 한 셀, 블록 크기와 다르다면, 바이트로 된
      L1 데이터 캐시 라인 크기
    – i-cache-line-size : 한 셀, 블록 크기와 다르다면, 바이트로 된
      L1 명령 캐시 라인 크기

  CPU 들을 소프트-리셋하는데 사용되는 메카니즘에 관한 어떤 정보같은
  여러분의 보드에 관련되어 찾은 어떤 프로퍼티를 추가하는 것도 괜찮습니다.
  예를 들면, 애플은 그들을 소프트 리셋함으로써 두번째 CPU들을 시작하면서
  CPU 소프트 리셋 라인을 위한 GPIO 넘버를 “soft-reset” 프로퍼티로
  넣었습니다.


  d) /memory 노드(들)

  여러분 보드의 물리 메모리 배치를 정의하기 위해서, 여러분은 하나 또는
  그 이상의 memory 노드(들)을 생성해야 합니다. 여러분은 원하는대로,
  하나의 노드에 모든 메모리 범위를 그 reg 프로퍼티에 넣거나, 또는 여러
  노드를 생성할 수 있습니다. 전체 경로에 사용되는 유닛 주소(@ 부분)는
  주어진 노드에 의해 정의된 메모리의 첫번째 범위의 주소입니다. 여러분이
  하나의 메모리 노드를 사용한다면, 일반적으로 @0이 될 것입니다.
  
  필수 프로퍼티:

    – device_type : “memory”가 되어야 함.
    – reg : 이 프로퍼티는 여러분 보드의 모든 물리 메모리 범위를 갖습니다.
      루트 노드의 #address-cells와 #size-cells로 각각 정의된 셀들을
      모두 이어 붙인 주소들/크기들의 하나의 리스트입니다. 예를 들면,
      이들 프로퍼티 둘 다 앞에서 주어진 예제처럼 2를 갖고, 6Gb 램의
      970 베이스 모델이 보통 다음처럼 여기서 “reg” 프로퍼티를 갖을 수
      있습니다:

      00000000 00000000 00000000 80000000
      00000001 00000000 00000001 00000000

      이것은 0에서 시작하는 0x80000000 바이트 범위와 0x10000000에서
      시작하는 0x10000000 바이트 범위입니다. 여러분은 2Gb 과 4Gb 사이의
      IO 홀을 대신하는 메모리가 없음을 볼 수 있습니다. 어떤 업체는
      더 작은 세그먼트로 이들 범위를 나누는 것을 더 선호합니다만,
      커널은 신경쓰지 않습니다.

  e) /chosen 노드

  이 노드는 약간 “특별”합니다. 보통, 오픈 펌웨어가 인자들이나, 또는
  기본 입/출력 디바이스와 같은 어떤 환경 변수 정보를 두는 곳입니다.

  이 명세는 약간의 필수 요소를 갖습니다만, 또한 보통 OF 클라이언트
  인터페이스로 부팅할 때 prom_init() 분기 코드에 의해 재구성되는 몇가지
  리눅스에만 필요한 프로퍼티를 정의합니다. 그러나 여러분은 평면화
  형식을 사용했을 때 여러분 스스로 제공해야만 합니다.

  권장 프로퍼티:

    – bootargs : 이것은 커널로 전달되는 0으로 끝나는 문자열입니다.
    – linux,stdout-path : 이것은 만약 있다면, 여러분의 표준 콘솔
      디바이스의 전체 경로입니다. 보통, 여러분 보드에 시리얼 디바이스가
      있다면, 커널이 그를 기본 콘솔로 고르도록 여기서 펌웨어 내에
      기본 콘솔로 그 하나를 전체 경로를 두고 싶을 것입니다.

  u-boot 은 그를 사용하는 플랫폼을 위해 chosen 노드를 생성하고 내부를
  채움을 알아두세요.

  (알림: 지금은 구식이 된 관례는 주 인터럽트 컨트롤러를 가리키는
  phandle 값을 가지는 interrupt-controller 로 불리는 프로퍼티를
  /chosen 아래에 포함하는 것입니다)

  f) /soc<SOC이름> 노드

  이 노드는 시스템-온-칩(SOC)를 표현하는데 사용되고, 프로세서가 SoC라면
  반드시 있어야 합니다. 최고 레벨 soc 노드는 SoC 상의 모든 디바이스에
  대한 전역 정보를 포함합니다. 노드 이름은 SoC의 메모리-맵핑된
  레지스터S의 기초 주소인 SoC의 유닛 주소를 포함해야 합니다. SoC의
  이름은 “soc”로 시작해야하고, 나머지 이름은 soc의 부품 번호를 나타내야
  합니다. 예를 들면, MPC8540의 soc 노드는 “soc8540″이 될 겁니다.
  

  필수 프로퍼티:

    – ranges : 메모리 맵핑된 SoC 레지스터의 SoC 주소의 변환을
      설명하도록 지정하여 정의되어야 함.
    – bus-frequency: SoC 노드의 버스 주파수를 포함. 보통 이 필드의
      값은 부트 로더에 의해 채워집니다.
    – compatible : SoC의 정확한 모델


  권장 프로퍼티:

    – reg : 이 프로퍼티는 SOC 노드 그 자체적으로 사용되는 메모리 맵핑된
      레지스터들의 주소와 크기를 정의합니다. 자식 디바이스 레지스터들은
      포함하지 않습니다 – 이들은 각 자식 노드 내에 정의될 것입니다.
      그 “reg” 프로퍼티 내에 지정된 주소는 SOC 노드의 유닛 주소와
      맞아야 합니다.
    – #address-cells : “soc” 디바이스의 주소 표현. 이 필드의 형식은
      디바이스 레지스터들이 메모리 맵핑됐느냐에 따라 다양할 것입니다.
      메모리 맵핑된 레지스터들에 대해서 이 필드는 레지스터들의 주소를
      나타내는데 필요한 셀의 수를 나타낼 것입니다. MMIO를 사용하지 않는
      SOC에서는 필요한 정보를 나타내는 충분한 셀을 포함하는 특별한 주소
      형식으로 정의되어져야 합니다. #address-cells를 정의하는데 대한
      자세한 사항은 위의 1) 을 보세요.
    – #size-cells : “soc” 디바이스의 크기 표현
    – #interrupt-cells : 인터럽트를 표현하는데 사용되는 셀의 너비를
      정의합니다. 보통 이 값은 인터럽트 번호를 표현하는 32비트 수 하나와
      인터럽트 감지와 레벨을 표현하는 32비트 수 하나를 포함하는 <2>
      입니다. 이 필드는 오직 SOC가 인터럽트 컨트롤러를 포함할 때만
      포함됩니다.

  SOC 노드는 플랫폼이 사용하는 각 개별 SOC 디바이스를 위한 자식 노드들을
  포함할 것 입니다. 노드들은 SOC 상에 존재는 하지만 특정 플랫폼에서
  사용되는 디바이스들은 생성되지 않아야 합니다. SOC 의 일부인 디바이스를
  어떻게 지정하는지에 대한 더 많은 정보는 챕터 VI를 보세요.

  MPC8540의 SOC 노드 예제:

        soc8540@e0000000 {
                #address-cells = <1>;
                #size-cells = <1>;
                #interrupt-cells = <2>;
                device_type = “soc”;
                ranges = <0x00000000 0xe0000000 0x00100000>
                reg = <0xe0000000 0x00003000>;
                bus-frequency = <0>;
        }



IV – “dtc”, 디바이스 트리 컴파일러(device tree compiler)
========================================================


dtc 소스 코드는 다음에서 찾을 수 있습니다.
<http://git.jdl.com/gitweb/?p=dtc.git>

경고: 이 버전은 아직 초기 개발 단계에 있습니다; 결과 디바이스 트리
“바이너리 객체”는 커널에 아직 유효하지 않습니다. 현재 생성되는 블록은
무엇보다도 유용한 예약 맵(빈 것을 생성하도록 고쳐질 것이고 부트로더가
채울 것입니다)이 없습니다. 에러 처리도 작업이 필요하고, 버그들이
숨어 있고, 또…


dtc는 기본적으로 주어진 형식의 디바이스-트리를 취해서 다른 형식으로
디바이스-트리를 출력합니다. 현재 제안되는 형식은 다음과 같습니다:

  입력 형식:
  ———-

     – “dtb”: “바이너리 객체” 형식, 즉, 바이너리 객체 내의 모든 헤더로
       된 평면 디바이스-트리 블록
     – “dts”: “소스” 형식. 이것은 디바이스-트리의 “소스”를 포함하는
        텍스트 파일입니다. 이 형식은 이 챕터 이후에 정의됩니다.
     – “fs” 형식. 이것은 노드들이 디렉토리들이고, 프로퍼티들이 파일들인
        /proc/device-tree 의 출력과 동등한 표현입니다.
        

 출력 형식:
 ———-

     – “dtb”: “바이너리 객체” 형식
     – “dts”: “소스” 형식
     – “asm”: 어셈블리 언어 파일. 이것은 디바이스-트리 “바이너리 객체”
       를 생성하는 가스에 의해 얻어질 수 있는 파일입니다. 이 파일은
       간단히 여러분의 Makefile에 붙을 수 있습니다. 추가적으로, 그
       어셈블리 파일은 사용될 수 있는 심볼들을 노출합니다.


dtc 툴의 문법은

    dtc [-I <입력-형식>] [-O <출력-형식>]
        [-o 출력-파일이름] [-V 출력_버전] 입력_파일이름


“출력_버전”은 생성될 “바이너리 객체” 형식이 무슨 버전인지 정의합니다.
지원되는 버전은 1,2,3, 그리고 16입니다. 기본은 현저 버전 3 이지만,
지나면 버전 16 으로 바뀔 겁니다.

덧붙여, dtc는 linux의 유일성, phandle 프로퍼티들, 문자열들의 유효함,
기타… 와 같은 트리 상의 다양한 자체 검사를 수행합니다.

.dts “소스” 파일은 C 와 C++ 스타일 주석을 지원하는 “C” 같은 형식이다.

/ {
}

위는 “디바이스-트리” 정의이다. 최고 레벨에 현재 지원되는 단하나의
문장입니다.

/ {
  property1 = “string_value”;   /* 0으로 끝나는 문자열을 포함하는
                                 * 프로퍼티를 정의
                                 */

  property2 = <0x1234abcd>;     /* 수로 된 32비트 값(16진수)을
                                 * 포함하는 프로퍼티를 정의
                                 */
                                 

  property3 = <0x12345678 0x12345678 0xdeadbeef>;
                                /* 수로 된 32비트 16진수 값들(셀들)
                                 * 3 개를 포함하는 프로퍼티를 정의
                                 */
  property4 = [0x0a 0x0b 0x0c 0x0d 0xde 0xea 0xad 0xbe 0xef];
                                /* 내용이 바이트들의 임의의 배열인
                                 * 프로퍼티를 정의
                                 */

  childnode@address {   /* 유닛 이름이 “address의 childnode”인
                                 * “childnode” 가 이름인 자식노드를
                                 * 정의
                                 */

    childprop = “hello\n”;      /* 자식 노드의 “childprop” 프로퍼티를
                                 * 정의(이 경우는 문자열)
                                 */
  };
};

노드들은 다른 노드들, 기타… 를 포함할 수 있어서 트리의 계층적 구조를
정의합니다.

문자열은 C에서의 일반적인 특수 문자들을 지원합니다: “\n”, “\t”, “\r”,
“\(8진수 값)”, “\x(16진수 값)”.

또한, cpp (gcc 전처리기)를 통해 여러분의 소스파일을 파이프로 흘려보낼
수 있어서 #include 의 것, 상수를 위한 #define, 기타… 를 사용할 수
있습니다.

마지막으로, phandle의 자동 생성, labels(asm 파일로 노출되어 여러분이
프로퍼티 내용을 가리킬 수 있고, 디바이스 트리로 연결한 어떠한 것으로부터
쉽게 변경할 수 있음), 어떤 셀들 안의 수로 된 값 대신 노드(컴파일 타임에
phandle에 의해 대체됨)를 “가리키는” label 또는 path, asm 파일로 예약 맵
주소를 노출하기, 컴파일 타임에 예약 맵 내용을 지정하는 능력, 기타…
같은 다양한 옵션들이 계획되어 있지만, 아직 구현되어 있지는 않습니다.

컴파일러에 구조 정의들의 개념을 추가하는 것이 더 낫겠지만, 우리는
.h 를 포함하는 파일을 (PCI 프로퍼티들 또는 인터럽트 맵 같은)몇가지
프로퍼티를 유용하게 감지하는 일반적인 정의들로 제공할 것입니다.


V – 부트로더에 대한 권장 사항
=============================


여기에 다양한 몇가지 제안되었던 생각들/권장 사항들이 있습니다. 이 모든
것들이 정의되고 구현되었습니다.

  – 부트로더는 디바이스-트리 그 자체를 사용할 수 있기를 원하고,
    조작하기(물리 메모리 크기나 커널 인자들 같은, 어떤 프로퍼티를
    추가하고/편집하고)를 원합니다. 이 시점에서 두가지 선택이 있을 수
    있습니다. 부트로더가 직접 평면화 형식 상에서 작업하거나, 또는,
    부트로더가 그 자신의 포인터들로 된(커널 것과 비슷한) 내부 트리
    표현을 갖거나 말입니다. 앞에 것이 편집/수정에 약간 더 어렵고,
    뒤에 것이 아마도 트리 구조를 처리하는 약간 더 복잡한 코드를
    필요로 합니다. 상대적으로 프로퍼티나 노드들의 “추가”가 쉽고,
    그저 메모리 이동으로 삭제하는것이 쉽게 설계되었습니다. 이런
    목적으로 내부 오프셋이나 포인터를 포함하지 않습니다.

  – 평면화 트리 형식으로부터 직접 노드들을 열거하고, 프로퍼티를
    탐색하기 위한 예제 코드는 커널 파일 drivers/of/fdt.c 안에서 찾을
    수 있습니다. of_scan_flat_dt() 함수, early_init_devtree() 안의
    그 사용 예, 그리고, 해당하는 다양한 early_init_dt_scan_*() 콜백들
    을 살펴 보세요. 그 코드는 GPL 부트로더에서 재사용 될 수 있고,
    그 코드의 작성자로서, 저는 GPL이 아닌 부트로더로 이 코드의 일부,
    또는 전부를 통합하길 원하는 업체들과 자유 라이센스를 토론할 수
    있다는 것이 행복합니다(참고가 필요합니다; 여기서 누가 ‘나’인가?
    (who is ‘I’ here?) —gcl 2011년 1월 31일)



VI – 시스템-온-칩 디바이스와 노드들
===================================

많은 회사들이 지금 프로세서 코어(CPU)와 많은 주변 장치가 한 조각의
실리콘 상에 존재하는 시스템-온-칩 프로세서 개발을 시작하고 있습니다.
이들 SOC들에 대해, SOC 노드는 SOC를 구성하는 디바이스들에 대한 자식
노드들을 정의하는데 사용되어야 합니다. 플랫폼들이 커널을 부팅하기 위해
이 모델을 사용하는 것이 필요하지 않은데 반해, 모든 SOC 구현들은
SOC 상의 디바이스들을 기술하는데 가능한 한 완전한 평면-디바이스-트리로
정의하고자 장려됩니다. 이것은 많은 양의 커널 코드의 일반화를 가능하게
할 것입니다.


1) SOC의 자식 노드들 정의하기
—————————–

SOC의 일부인 각 개별 디바이스들은 SOC 노드 내에 그들만의 노드 항목을
가질 것입니다. SOC 내에 포함된 각 개별 디바이스들을 위해, 유닛 주소
프로퍼티는 이 디바이스의 부모의 주소 공간 안의 메모리 맵핑된
레지스터들을 위한 주소 오프셋을 나타냅니다. 그 부모의 주소 공간은
최고 레벨 soc 노드 내의 “ranges” 프로퍼티로 정의됩니다. SOC 노드 아래에
직접 존재하는 각 개별 노드의 “reg” 프로퍼티는 자식 주소 공간으로부터
부모 SOC 주소 공간으로의 주소 맵핑, 디바이스의 메모리-맵핑된 레지스터
파일의 크기를 포함해야만 합니다.

SOC 내부에 존재하는 많은 디바이스들을 위해, 디바이스 트리 노드의 형식을
미리 정의하는 명세들이 있습니다. 모든 SOC 자식 노드들은 이 문서에서
알리는 곳 외에는 이 명세를 따라야 합니다.

MPC8540의 일부 SOC 노드 정의 예제를 부록 A에서 보세요.


2) 현재의 OF 명세없이 디바이스들 나타내기
—————————————–

현재, 주로 보드들이 현재 오픈 펌웨어를 사용하여 부팅되지 않는 SoC들을
포함하고 있기 때문에, 오픈 펌웨어 명세의 일부로 정의된 표준 표현이 없는
SOC 상의 많은 디바이스들이 있습니다. 새로운 디바이스의 바인딩 문서는
Documentation/devicetree/bindings 디렉토리에 추가되어야 합니다.
그 디렉토리는 더 많고 많은 SoC들에 디바이스 트리 지원을 확장할 것입니다.


VII – 디바이스들의 인터럽트 정보 지정하기
=========================================

디바이스 트리는 하드웨어 시스템의 버스들과 디바이스들을 하드웨어의
물리적 버스 토폴로지와 유사한 형식으로 표현합니다.

추가로, 하드웨어 내의 인터럽트의 계층과 경로를 표현하는 논리적인
‘인터럽트 트리’가 존재합니다.

인터럽트 트리 모델은 “오픈 펌웨어 권장 용례: 인터럽트 맵핑 버전 0.9
(Open Firmware Recommended Practice: Interrupt Mapping Version 0.9)”
안에 완전히 서술되어 있습니다. 이 문서는
<http://www.openfirmware.org/ofwg/practice/> 에서 이용 가능합니다.

1) interrupt 프로퍼티
———————

하나의 인터럽트 컨트롤러로 인터럽트를 생성하는 디바이스들은 
OF 인터럽트 맵핑 문서 내에 서술된 관례적인 OF 표현을 사용해야 합니다.

인터럽트를 생성하는 각 개별 디바이스는 ‘interrupt’ 프로퍼티를 가져야만
합니다. 인터럽트 프로퍼티 값은 디바이스의 그 인터럽트 또는 인터럽트들을
서술하는 임의의 개수의 ‘interrupt specifier’ 값들입니다.

인터럽트 지시자의 인코딩은 디바이스가 인터럽트 트리 내에 위치한
인터럽트 도메인에 따라 결정됩니다. 인터럽트 도메인의 루트는 그의
#interrupt-cells 프로퍼티 내에 인터럽트 지시자를 인코딩하는데 필요한
32비트 셀들의 수를 정합니다. 도메인들의 더 자시한 설명은 OF 인터럽트
맵핑 문서를 보세요.

예를 들면, OpenPIC 인터럽트 컨트롤러의 바인딩은 #interrupt-cells 값으로
인터럽트 번호와 레벨/감지 정보를 위해 2를 지정합니다. OpenPIC 인터럽트
도메인 내의 모든 인터럽트 자식들은 그들의 인터럽트 프로퍼티 내에
인터럽트 당 2 셀을 사용합니다.

PCI 버스 바인딩은 어느 인터럽트 핀(INTA,INTB,INTC,INTD)이 사용되는지
인코딩하기 위해서 #interrupt-cell 값으로 1을 지정합니다.

2) interrupt-parent 프로퍼티
—————————-

interrupt-parent 프로퍼티는 인터럽트 트리 내의 디바이스 노드와 그의
인터럽트 부모 간의 명시적인 링크를 정의하기 위해서 지정됩니다. 이
interrupt-parent 값은 부모 노드의 phandle 입니다.

interrupt-parent 프로퍼티가 한 노드를 정의하지 않으면, 그의 인터럽트
부모는 그 노드의 _디바이스 트리_ 계층 내의 조상으로 추정합니다.

3) OpenPIC 인터럽트 컨트롤러
—————————-

OpenPIC 인터럽트 컨트롤러는 인터럽트 정보를 인코딩하는데 2셀을
필요로 합니다. 첫번째 셀은 인터럽트 번호를 정의합니다. 두번째 셀은
감지와 레벨 정보를 정의합니다.

감지와 레벨 정보는 다음처럼 인코딩되어야 합니다:

        0 = 로우(Low)에서 하이(High)로의 엣지(Edge) 감지 타입이 활성
        1 = 엑티브 로우(active low) 레벨(level) 감지 타입이 활성
        2 = 엑티브 하이(active high) 레벨 감지 타입이 활성
        3 = 하이에서 로우로의 엣지 감지 타입이 활성

4) ISA 인터럽트 컨트롤러
————————

ISA PIC 인터럽트 컨트롤러는 인터럽트 정보를 인코딩하는데 2셀을
필요로 합니다. 첫번째 셀은 인터럽트 번호를 정의합니다. 두번째 셀은
감지와 레벨 정보를 정의합니다.

ISA PIC 인터럽트 컨트롤러는 다음에 나열된 ISA PIC 인코딩으로 붙어야
합니다.

        0 = 엑티브 로우(active low) 레벨(level) 감지 타입이 활성
        1 = 엑티브 하이(active high) 레벨 감지 타입이 활성
        2 = 하이(High)에서 로우(Low)로의 엣지(Edge) 감지 타입이 활성
        3 = 로우에서 하이로의 엣지 감지 타입이 활성

VIII – 디바이스 전원 관리 정보 지정하기 (sleep 프로퍼티)
========================================================

SoC 상의 디바이스들은 그 디바이스의 자체 레지스터 블록으로부터 떨어져나가는
저-전력 상태로 디바이스를 두는 메카니즘을 종종 갖고 있습니다. 어떤 때는,
이 정보가 cell-index 프로퍼티가 꽤 설명할 수 있는 것보다 좀 복잡합니다.
그래서 각 이런 형태의 개별 디바이스는 이들 연결을 서술하는 “sleep”
프로퍼티를 포함할 것 입니다.

sleep 프로퍼티는 하나 또는 그 이상의 각각이 슬립 컨트롤러로의 phandle,
그 뒤에 0 또는 그 이상의 셀의 컨트롤러에-따른(contrller-specific)
슬립 지시자로 구성된, 슬립 자원들로 구성됩니다.

가능한 저전력 모드가 무슨 종류인지의 의미들은 슬립 컨트롤러에 의해
정의됩니다. 지원될 저전력 모드의 타입 예제는:

 – 다이내믹(Dynamic): 디바이스가 아무때나 꺼지고 켜짐.
 – 시스템 서스펜드(System Suspend): 디바이스가 꺼지거나 시스템 서스펜드
   동안 깬 상태로 남아있도록 요청할 것입니다.
 – 퍼머넌트(Permanent): 디바이스가 영원히 꺼집니다(다음 하드 리셋 전까지)

어떤 디바이스는 각각 다른 것들과 클럭 도메인을 공유할 것 입니다. 그런 것들은
디바이스가 사용 중인 것이 없을 때에만 서스펜드 상태가 됩니다. 정당할 때는,
이들 노드들이 버스가 sleep 프로퍼티를 갖는 곳에서 가상 버스 상에 있어야만
합니다. 클럭 도메인이 이런 형태로 꽤 그룹 지어진 디바이스들 간에 공유되면,
(그 필요성이 나타나기 전까지 표준화된 슬립-맵을 정의하는 것을 기다려야 하는
것을 제외하면, 인터럽트 넥서스와 유사하게)가상 슬립 컨트롤러를 생성해야
합니다.

부록 A – MPC8540의 예제 SOC 노드
================================

        soc@e0000000 {
                #address-cells = <1>;
                #size-cells = <1>;
                compatible = “fsl,mpc8540-ccsr”, “simple-bus”;
                device_type = “soc”;
                ranges = <0x00000000 0xe0000000 0x00100000>
                bus-frequency = <0>;
                interrupt-parent = <&pic>;

                ethernet@24000 {
                        #address-cells = <1>;
                        #size-cells = <1>;
                        device_type = “network”;
                        model = “TSEC”;
                        compatible = “gianfar”, “simple-bus”;
                        reg = <0x24000 0x1000>;
                        local-mac-address = [ 0x00 0xE0 0x0C 0x00 0x73 0x00 ];
                        interrupts = <0x29 2 0x30 2 0x34 2>;
                        phy-handle = <&phy0>;
                        sleep = <&pmc 0x00000080>;
                        ranges;

                        mdio@24520 {
                                reg = <0x24520 0x20>;
                                compatible = “fsl,gianfar-mdio”;

                                phy0: ethernet-phy@0 {
                                        interrupts = <5 1>;
                                        reg = <0>;
                                        device_type = “ethernet-phy”;
                                };

                                phy1: ethernet-phy@1 {
                                        interrupts = <5 1>;
                                        reg = <1>;
                                        device_type = “ethernet-phy”;
                                };

                                phy3: ethernet-phy@3 {
                                        interrupts = <7 1>;
                                        reg = <3>;
                                        device_type = “ethernet-phy”;
                                };
                        };
                };

                ethernet@25000 {
                        device_type = “network”;
                        model = “TSEC”;
                        compatible = “gianfar”;
                        reg = <0x25000 0x1000>;
                        local-mac-address = [ 0x00 0xE0 0x0C 0x00 0x73 0x01 ];
                        interrupts = <0x13 2 0x14 2 0x18 2>;
                        phy-handle = <&phy1>;
                        sleep = <&pmc 0x00000040>;
                };

                ethernet@26000 {
                        device_type = “network”;
                        model = “FEC”;
                        compatible = “gianfar”;
                        reg = <0x26000 0x1000>;
                        local-mac-address = [ 0x00 0xE0 0x0C 0x00 0x73 0x02 ];
                        interrupts = <0x41 2>;
                        phy-handle = <&phy3>;
                        sleep = <&pmc 0x00000020>;
                };

                serial@4500 {
                        #address-cells = <1>;
                        #size-cells = <1>;
                        compatible = “fsl,mpc8540-duart”, “simple-bus”;
                        sleep = <&pmc 0x00000002>;
                        ranges;

                        serial@4500 {
                                device_type = “serial”;
                                compatible = “ns16550”;
                                reg = <0x4500 0x100>;
                                clock-frequency = <0>;
                                interrupts = <0x42 2>;
                        };

                        serial@4600 {
                                device_type = “serial”;
                                compatible = “ns16550”;
                                reg = <0x4600 0x100>;
                                clock-frequency = <0>;
                                interrupts = <0x42 2>;
                        };
                };

                pic: pic@40000 {
                        interrupt-controller;
                        #address-cells = <0>;
                        #interrupt-cells = <2>;
                        reg = <0x40000 0x40000>;
                        compatible = “chrp,open-pic”;
                        device_type = “open-pic”;
                };

                i2c@3000 {
                        interrupts = <0x43 2>;
                        reg = <0x3000 0x100>;
                        compatible  = “fsl-i2c”;
                        dfsrr;
                        sleep = <&pmc 0x00000004>;
                };

                pmc: power@e0070 {
                        compatible = “fsl,mpc8540-pmc”, “fsl,mpc8548-pmc”;
                        reg = <0xe0070 0x20>;
                };
        };

[Linux:Kernel] 지연시간 – 다양한 커널 딜레이(delay) / 슬립(sleep) 메카니즘의 정보

이 문서의 저작권은 GPL 라이센스를 따릅니다(This document is released under the GPL license).

Documentation/timers/timers-howto.txt

번역: 양정석(dasomoli@gmailREMOVETHIS.com)

지연시간 – 다양한 커널 딜레이(delay) / 슬립(sleep) 메카니즘의 정보

——————————————————————
이 문서는 공통적인 질문에 대해 답변하려고 합니다: “무엇이 지연 시간을
추가하는 옳은 방법(RightWay (TM)) 인가?”
이 질문은 하드웨어 지연 시간을 처리해야만 하지만, 커널의 내부
동작과는 거의 직접적으로 관련이 없을 드라이버 작성자들에 의해서
거의 제기됩니다.
딜레이 추가하기
—————
먼저, 가장 중요한, 여러분에게 물을 필요가 있는 질문은 “내 코드가
어토믹 컨텍스트(atomic context) 안에 있는가?” 입니다. 이것은 “정말로
어토믹 컨텍스트 안의 딜레이가 필요한가”라는 질문이 따라 붙어야 합니다.
만약 그렇다면…
어토믹 컨텍스트:
여러분은 *delay 계열의 함수를 사용해야만 합니다. 이들 함수는
클럭 속도의 지피(jiffie) 추정하기를 사용하고, 원하는 지연
시간을 채우기 위해서 충분한 루프 사이클을 바쁘게 대기(busy
wait)할 것입니다:
ndelay(unsigned long nsecs)
udelay(unsigned long usecs)
mdelay(unsigned long msecs)
udelay 는 일반적으로 선호되는 API 입니다; ndelay-레벨
정밀도는 많은 PC가 아닌(non-PC) 기기들에서 실제로 존재하지
않을 것입니다.
mdelay 는 udelay에 큰 인자를 주었을 때 가능한 오버 플로우
(overflow)를 세는, 돌고 도는 udelay의 매크로입니다.
일반적으로, mdelay의 사용은 권장되지 않고, 코드는 msleep를
사용할 수 있도록 리팩토링되는 것이 좋습니다.
어토믹이 아닌(NON-ATOMIC) 컨텍스트:
여러분은 *sleep[_range] 계열의 함수를 사용하는 것이 좋습니다.
여기에 몇가지 더 많은 옵션이 있습니다. 여기 있는 어떤 것들도
정확히 동작할 것이긴 하지만, “알맞은” 슬립 함수를 사용하는 것은
스케줄러와 전력 관리를 돕고, 여러분의 드라이버를 더 좋게
만들어줄 것입니다. 🙂
— 바쁜 대기(busy-wait)루프로 뒷받침:
udelay(unsigned long usecs)
— hrtimers로 뒷받침:
usleep_range(unsigned long min, unsigned long max)
— 지피 / 기존 타이머(legacy timer)로 뒷받침:
msleep(unsigned long msecs)
msleep_interruptible(unsigned long msecs)
*dealy 계열과 다르게, 이들 각각의 호출을 수행하는 바닥에 깔린
메카니즘이 다양하기 때문에 여러분이 알고 있어야 할 특성이 있습니다.
적은 USECS ( < ~10us? ) 를 위한 슬립:
* udelay를 사용하세요.
– usleep은 왜 아닌가요?
느린 시스템에서, (임베디드, 또는 아마도 스피드-스텝
PC!) usleep 을 위해서 hrtimers를 셋팅하는 오버헤드
(overhead)가 *아마* 그만큼 가치가 없을 것입니다. 앞과
같은 측정은 명확히 여러분의 재원적 사항에 달려 있습니다만,
알아둘 만 한 것은 아닙니다.
~USECS 나 작은 MSECS ( 10us – 20ms ) 를 위한 슬립:
* usleep_range를 사용하세요.
– (1ms – 20ms)를 위한 msleep은 왜 아닌가요?
원래는 여기서 설명했습니다:
http://lkml.org/lkml/2007/8/3/250
msleep(1~20) 은 호출자가 의도한 것을 하지 않을 수 있고,
종종 더 길게(1~20ms 사이의 어떤 값에 대해서도 실제
~20 ms 슬립) 슬립할 것입니다. 많은 경우에 이것은 원하는
동작이 아닙니다.
– “usleep” 은 왜 없나요 / 좋은 범위는 무엇인가요?
usleep_range 는 hrtimers의 꼭대기에서 빌드되므로, 그
웨이크업(wakeup)은 매우 정밀할 것입니다. 그래서 간단한
usleep 함수는 거의 많은 수의 원치 않는 인터럽트를 일으킵니다.
범위의 도입으로, 스케줄러는 여러분의 웨이크업을 다른 이유로
인해 일어날 수 있는, 혹은, 최악의 경우에 여러분의
상한(upper bound)을 위한 인터럽트를 점화하는 다른
웨이크업과 합치는데 자유롭게 됩니다.
여러분이 범위를 더 넓게 할 수록, 여러분이 인터럽트를
일으키지 않을 기회가 더 커집니다; 이것은 여러분의 코드
경로에서 지연 시간/성능 상의 허락할만한 상한값과
균형을 맞추어야 합니다. 여기에서 정확한 허용값은 매우 상황에
의존적입니다. 그래서 이는 호출자에게 이유가 있는 범위를
결정하도록 남겨둡니다.
더 큰 MSEC ( 10ms+ ) 를 위한 슬립
* msleep 이나 가능하면 msleep_interruptible 을 사용하세요.
– 무슨 차이인가요?
msleep 은 현재 태스크를 TASK_UNINTERRUPTIBLE 로 셋팅하는데
반해, msleep_interruptible 은 현재 태스크를 그 슬립을
스케줄링하기 전에 TASK_INTERRUPTIBLE 로 셋팅합니다.
요약하면, 차이는 슬립이 시그널에 의해 일찍 끝날 수 있는지
입니다. 일반적으로, 여러분이 인터럽트 가능한 파생 형태가
필요하다는 것을 알기 전까지는 그냥 msleep을 사용하세요.

[Android] ${ro.hardware}를 사용한 하드웨어 별 init.rc 처리

안 적어 놓으니 역시나 까먹는다. 아래는 ARM 32비트에 국한한다.

커널 부팅의 마지막에 init 프로세스가 시작되면, 램디스크 안에 든 init.rc 파일을 읽어 처리하게 된다. 별 다른 처리를 하지 않았다면, 기본적으로 system/core/rootdir/init.rc 파일이 램디스크로 복사되므로 이 파일을 읽어 처리한다.
이 init.rc 파일에서는 ro.hardware 프로퍼티 값에 따라 init.${ro.hardware}.rc 파일을 읽어 처리하게 되는데 이 파일로 각 보드마다 다른 처리를 가능하게 한다.
여기서 ro.hardware 프로퍼티 값은 init(system/core/init) 안에서 다음과 같은 순서로 셋팅한다.

1. /proc/cpuinfo 를 읽어서 이 안의 “Hardware:” 뒤의 값을 소문자로 모두 바꿔 사용(system/core/init/util.c 의 get_hardware_name())
2. 부트로더에서 cmdline 으로 androidboot.hardware 값이 넘어왔다면, ro.boot.hardware 프로퍼티로 셋팅한다(system/core/init/init.c 의 import_kernel_nv()).[ro.boot.hardware 값은 꼭 부트로더에서 넘어오지 않을 수도 있다]
3. ro.boot.hardware 프로퍼티 값이 있다면, 앞에서 /proc/cpuinfo 에서 읽은 값을 오버라이드한다.

위의 순서를 거쳐 셋팅된 ro.hardware 프로퍼티를 사용해서 init.${ro.hardware}.rc 파일을 읽게 된다.

1. 의 경우를 사용할 때는 /proc/cpuinfo 값을 사용하므로, 리눅스 커널의 사용되는 struct machine_desc 안의 .name 을 사용하게 된다.
/proc/cpuinfo 에서 “Hardware: ” 를 출력하는 부분은 리눅스 커널 소스의 arch/arm/kernel/setup.c 안의 c_show() 함수를 보면 되는데, 다음과 같다.

static int c_show(struct seq_file *m, void *v)
{
        …

        seq_printf(m, “Hardware\t: %s\n”, machine_name);

        …

machine_name 은 커널 부팅 중 (arch/arm/kernel/setup.c 의) setup_arch() 에서 다음처럼 셋팅한다.

void __init setup_arch(char **cmdline_p)
{

        …
        machine_name = mdesc->name;

        …

struct machine_desc 안의 .name 은 대부분 머신 파일 안에서 MACHINE_START(_type, _name) 매크로를 통해 선언한다. _name이 .name이 된다. 그러므로 결국 _name 에 들어가는 값이 읽혀 소문자로 바뀌어 사용된다. 예를 들면, Nexus 10 디바이스의 경우 다음과 같이 arch/arm/mach-exynos/board-manta.c 안에 다음과 같이 정의되어 있다. 여기서는 “Manta” 가 소문자로 모두 바뀌어 manta 로 사용된다.

MACHINE_START(MANTA, “Manta”)
        … 
MACHINE_END


참고로, MACHINE_START 매크로는 arch/arm/include/asm/mach/arch.h 에 있는데 다음과 같다.

#define MACHINE_START(_type,_name)                      \
static const struct machine_desc __mach_desc_##_type    \
 __used                                                 \
 __attribute__((__section__(“.arch.info.init”))) = {    \
        .nr             = MACH_TYPE_##_type,            \
        .name           = _name,
#define MACHINE_END                             \
};

2. 의 경우는 cmdline 을 통해 넘어오므로, 부트로더에서 ATAG를 통해 cmdline 에 셋팅하여 넘기는 것이 가장 정상적인 방법이고, 편법으로는 cmdline 을 바꿀 수 있는 여러가지 방법(예를 들면, 커널 컴파일 타임에 셋팅한다든지, boot.img 를 만들 때 cmdline 을 설정해 준다든지 등)을 통해 셋팅할 수 있다.(아니면 아예 ro.boot.hardware 값을 직접 셋팅해도 아마?)
androidboot.hardware 값이 ro.boot.hardware 프로퍼티 값이 되는 과정은 system/core/init/init.c 의 import_kernel_nv() 함수 내에서 처리된다.

static void import_kernel_nv(char *name, int for_emulator)
{                          
    …
    if (!strcmp(name,”qemu”)) {
        strlcpy(qemu, value, sizeof(qemu));
    } else if (!strncmp(name, “androidboot.”, 12) && name_len > 12) {
        const char *boot_prop_name = name + 12;
        char prop[PROP_NAME_MAX];
        int cnt;
        
        cnt = snprintf(prop, sizeof(prop), “ro.boot.%s”, boot_prop_name);
        if (cnt < PROP_NAME_MAX)
            property_set(prop, value);
    }
}

3. 의 ro.boot.hardware 프로퍼티 값이 ro.hardware 프로퍼티 값으로 셋팅되는 과정은 system/core/init/init.c 의 export_kernel_boot_props() 에서 수행된다. 이 과정 중에 ro.boot.hardware 프로퍼티 값이 있다면, 1. 의 /proc/cpuinfo 을 통해 얻은 값을 오버라이드하게 된다.

static void export_kernel_boot_props(void)
{
    … 
    /* if this was given on kernel command line, override what we read
     * before (e.g. from /proc/cpuinfo), if anything */
    ret = property_get(“ro.boot.hardware”, tmp);
    if (ret)
        strlcpy(hardware, tmp, sizeof(hardware));

    property_set(“ro.hardware”, hardware);  

[Linux:Kernel] 원형 버퍼(Circular Buffer)

이 문서의 저작권은 GPL 라이센스를 따릅니다(This document is released under the GPL license).

      =========
      원형 버퍼
      =========
작성: David Howells <dhowells@redhat.com>
      Paul E. McKenney <paulmck@linux.vnet.ibm.com>
번역: 양정석 <dasomoli@gmailREMOVETHIS.com>
리눅스는 원형 버퍼를 구현하는데 사용할 수 있는 많은 기능을 제공합니다.
다음의 두가지 셋이 있습니다:
 (1) 2의 거듭제곱 크기의 버퍼에 대한 정보를 결정하기 위한 편리한 함수들
 (2) 버퍼 안의 객체의 생산자와 소비자가 하나의 락(lock)을 공유하지 않기를
     원할 때를 위한 메모리 장벽
이들 기능을 사용하기 위해서, 아래에서 논의되는 것처럼, 적어도 하나의 생산자와
하나의 소비자가 있어야 합니다. 여러 생산자를 처리하는 것도 그들을 연속적으로
일렬로 늘어놓는 것으로, 여러 소비자를 처리하는 것도 그들을 연속적으로 일렬로
늘어놓는 것으로써 처리 가능합니다.
내용:
 (*) 원형 버퍼가 뭔가요?
 
 (*) 2의 거듭제곱 버퍼 측정.
 (*) 원형 버퍼와 함께 메모리 장벽 사용하기
     – 생산자.
     – 소비자.
===================
원형 버퍼가 뭔가요?
===================
가장 먼저, 원형 버퍼가 뭘까요? 원형 버퍼는 그 안에 두가지 인덱스가 있는 유한한
크기의 고정된 버퍼입니다:
 (1) ‘헤드(head)’ 인덱스 – 생산자가 항목을 버퍼로 집어넣는 곳
 (2) ‘테일(tail)’ 인덱스 – 소비자가 다음 항목을 버퍼에서 찾는 곳
일반적으로 테일 포인터가 헤드 포인터와 같을 때, 그 버퍼는 빈 것입니다; 그리고 그
버퍼는 헤드 포인터가 테일 포인터보다 하나 작을 때, 가득 찬 것입니다. 
헤드 인덱스는 아이템들이 추가될 때, 테일 인덱스는 아이템들이 제거될 때 증가합니다.
테일 인덱스는 헤드 인덱스를 절대로 넘어설 수 없고, 두 인덱스 모두 그들이 버퍼의
끝에 다다랐을 때, 0으로 다시 돌아와야 합니다. 그래서 무한한 양의 데이터가
그 버퍼를 통해 흐를 수 있습니다.
일반적으로 항목들은 모두 같은 단위 크기이지만, 아래 테크닉을 사용하는 것이
엄격하게 요구되지는 않습니다. 인덱스들은 여러 항목이나 가변 크기의 항목들이
두 인덱스 모두 다른 것을 추월하지 않도록 제공되는 그 버퍼 안으로 포함된다면,
1보다 더 많이 증가할 수 있습니다. 그러나 그 구현자는 한 단위 크기 이상의
한 부분은 버퍼의 끝을 돌 수 있고, 두 세그먼트로 쪼개질 수 있기 때문에 조심해야
합니다.
=======================
2의 거듭제곱 버퍼 측정
=======================
제멋대로인 크기의 원형 버퍼의 사용하거나 남아있는 양의 계산은 보통 나머지
(나누기) 명령의 사용을 필요로 하는 느린 동작이 됩니다. 그러나 버퍼가 2의
거듭제곱 크기라면, 훨씬 빠른 비트-앤드(bitwise-AND) 명령을 대신 사용할 수
있습니다.
리눅스는 2의 거듭 제곱 원형 버퍼를 처리하기 위한 매크로 셋을 제공합니다. 이들은
다음을 통해 사용할 수 있습니다:
#include <linux/circ_buf.h>
그 매크로들은:
  (*) 버퍼의 남은 양 측정:
CIRC_SPACE(head_index, tail_index, buffer_size);
     이것은 항목들이 넣어질 수 있는 그 버퍼[1] 안에 남은 공간의 양을 반환합니다.
 (*) 버퍼 안에 최대로 연이어 붙어있는 공간 측정:
CIRC_SPACE_TO_END(head_index, tail_index, buffer_size);
     이것은 항목들이 다시 그 버퍼의 처음으로 돌아가는 것 없이 즉시 삽입되어질
     수 있는 그 버퍼[1] 안에 남은 연이은 공간의 양을 반환합니다.
 (*) 버퍼의 사용량 측정:
CIRC_CNT(head_index, tail_index, buffer_size);
     이것은 현재 버퍼[2]를 사용하는 항목 수를 반환합니다
 (*) 처음으로 돌아가는 것이 없을 때(non-wrapping)의 버퍼 사용량 측정:
CIRC_CNT_TO_END(head_index, tail_index, buffer_size);
     이것은 다시 그 버퍼의 처음으로 돌아가는 것 없이 뽑아 낼 수 있는 연이은
     항목들[2]의 수를 반환합니다.
이들 매크로 각각은 명목상으로 0 에서 buffer_size-1 사이의 값을 반환할 것입니다.
그러나:
 [1] CIRC_SPACE*() 들은 생산자 안에서 사용되도록 의도되었습니다. 생산자에게
     그들은 생산자가 헤드 인덱스를 제어하기 때문에 하한값을 반환할 것입니다.
     그러나 소비자는 여전히 다른 CPU 상에서 그 버퍼를 감소시키고, 테일 인덱스를
     옮기고 있을 수 있습니다.
     생산자가 그 공간을 바쁘게 감소시킬 수 있기 때문에, 소비자에게 그것은
     상한값을 보여줄 것입니다.
 [2] CIRC_CNT*() 들은 소비자 안에서 사용되도록 의도되었습니다. 소비자에게
     그들은 소비자가 테일 인덱스를 제어하기 때문에 하한값을 반환할 것입니다.
     그러나 생산자는 여전히 다른 CPU 상에서 그 버퍼를 채우고, 헤드 인덱스를
     옮기고 있을 수 있습니다.
     소비자가 그 버퍼를 바쁘게 비울 수 있기 때문에, 생산자에게 그것은 상한값을
     보여줄 것입니다.
 [3] 서드 파티에게는, 생산자와 소비자가 보여질 수 있게 되어감에 의해, 그
     인덱스들에 쓰는 순서는 그들이 독립적이고, 다른 CPU 상에서 생성될 있기 때문에
     보장될 수 없습니다. 그래서 이런 상황에서의 결과는 그저 추정이 될 것이고, 아예
     틀릴 수도 있습니다.
=======================================
원형 버퍼와 함께 메모리 장벽 사용하기
=======================================
원형 버퍼와 함께 메모리 장벽을 사용함으로써, 여러분은 다음을 위한 욕구를
피할 수 있습니다.
 (1) 그 버퍼의 양 끝으로의 접근을 다스리기 위한 단일 락(lock) 사용, 그래서
     그 버퍼가 동시에 채우고 비울 수 있는; 그리고
 (2) 어토믹(atomic) 카운터 연산 사용
이를 위한 두 편이 있습니다: 그 버퍼를 채우는 생산자, 그를 비우는 소비자.
어느 한번에 하나만 버퍼를 채워야 하고, 어느 한번에 하나만 버퍼를 비워야 합니다만,
두 편은 동시에 수행할 수 있습니다.
생산자
——
생산자는 이처럼 보일 것입니다:
spin_lock(&producer_lock);
unsigned long head = buffer->head;
unsigned long tail = ACCESS_ONCE(buffer->tail);
if (CIRC_SPACE(head, tail, buffer->size) >= 1) {
/* 버퍼로 아이템 하나를 넣어라 */
struct item *item = buffer[head];
produce_item(item);
smp_wmb(); /* 헤드를 증가시키기 전에 항목을 넣어라 */
buffer->head = (head + 1) & (buffer->size – 1);
/* wake_up() 은 어느 하나가 깨기 전에 헤드가 제출됐음을 확인할
* 것이다 */
wake_up(consumer);
}
spin_unlock(&producer_lock);
이는 CPU가 새로운 항목의 내용을 헤드 인덱스가 소비자에게 사용가능하게 만들기
전에 반드시 쓰여져야 한다는 것을 지시할 것이고, 그 후 그 CPU가 소비자가 깨기
전에 바뀐 헤드 인덱스가 쓰여져야만 함을 지시합니다.
wake_up() 이 꼭 사용하는 그 메카니즘일 필요는 없지만, 만약 상태 변경이 일어난다면,
사용되는 아무 것이나 헤드 인덱스의 갱신과 소비자의 상태 변경 사이에 반드시 한번의
(쓰기) 메모리 장벽을 보장해야 함을 명심하세요.
소비자
——
소비자는 이처럼 보일 것입니다:
spin_lock(&consumer_lock);
unsigned long head = ACCESS_ONCE(buffer->head);
unsigned long tail = buffer->tail;
if (CIRC_CNT(head, tail, buffer->size) >= 1) {
/* 그 인덱스에 있는 내용을 읽기 전에 인덱스를 읽어라 */
smp_read_barrier_depends();
/* 버퍼로부터 하나의 항목을 꺼내라 */
struct item *item = buffer[tail];
consume_item(item);
smp_mb(); /* 테일을 증가시키기 전에 서술자 읽기를 끝내라 */
buffer->tail = (tail + 1) & (buffer->size – 1);
}
spin_unlock(&consumer_lock);
이는 CPU가 새로운 항목을 읽기 전에 그 인덱스가 올라갔음을 확인할 것을 지시할
것이고, 그 후 CPU가 그 항목을 지울 새로운 테일 포인터를 쓰기 전에 그 항목
읽기를 끝냈음을 확인하도록 합니다.
반대 인덱스를 읽기 위한 두 알고리듬 안에 ACCESS_ONCE() 의 사용에 주의하세요.
이것은 컴파일러가 그들의 캐시된 값-어떤 컴파일러는 smp_read_barrier_depends()를
가로질러 수행하는-을 버리고 재로딩하는 것을 막습니다. 여러분이 반대 인덱스가
한 번만 사용될 거라고 알 수 있다면, 이것이 엄격히 필요하지는 않습니다.
=============
더 읽을 거리
=============
리눅스의 메모리 장벽 기능에 대한 설명을 위해 Documentation/memory-barriers.txt도
보세요.

[Linux:Kernel] 어떻게 I2C 디바이스를 인스턴스로 만드는가(How to instantiate I2C devices)

이 문서의 라이센스는 GPL 을 따릅니다(This document is released under the GPL license).

Documentation/i2c/instantiating-devices

번역 : 양정석(dasomoli@gmailREMOVETHIS.com)

어떻게 I2C 디바이스를 인스턴스로 만드는가

=========================================
PCI나 USB 디바이스와 다르게, I2C 디바이스들은 하드웨어 레벨에서 열겨되지
않습니다. 대신에, 소프트웨어가 어떤 디바이스가 각 I2C 버스 세그먼트에
붙어 있는지, 어떤 주소를 이 디바이스가 사용하는지 알아야만 합니다. 이런
이유로, 커널 코드는 I2C 디바이스들을 명시적으로 인스턴스로 만듭니다.
컨텍스트와 요구 사항에 따라 이렇게 할 수 있는 여러 방법이 있습니다.
방법 1: I2C 디바이스를 버스 번호로 선언하기
——————————————-
이 방법은 많은 임베디드 시스템을 위한 경우처럼, I2C 버스가 하나의 시스템
버스일 때, 적절합니다. 이런 시스템 상에서, 각 I2C 버스는 미리 알려진
번호를 가집니다. 그래서 이 버스 상에서 활동하는 I2C 디바이스를 미리 선언
(pre-declare)하는 것이 가능합니다. 이것은 i2c_register_board_info() 를
호출함으로써 등록되는 struct i2c_board_info 의 배열로 수행됩니다.
예제(omap2 h4):
static struct i2c_board_info h4_i2c_board_info[] __initdata = {
{
I2C_BOARD_INFO(“isp1301_omap”, 0x2d),
.irq = OMAP_GPIO_IRQ(125),
},
{ /* EEPROM on mainboard */
I2C_BOARD_INFO(“24c01”, 0x52),
.platform_data = &m24c01,
},
{ /* EEPROM on cpu card */
I2C_BOARD_INFO(“24c01”, 0x57),
.platform_data = &m24c01,
},
};
static void __init omap_h4_init(void)
{
(…)
i2c_register_board_info(1, h4_i2c_board_info,
ARRAY_SIZE(h4_i2c_board_info));
(…)
}
위의 코드는 I2C 버스 1 상에 그들 각각의 주소와 그들 드라이버가 필요로 하는
각각에 맞는 데이터를 포함하는 3개의 디바이스를 선언합니다. 해당 I2C 버스가
등록될 때, I2C 디바이스들은 i2c-core에 의해 자동으로 인스턴스로 될 것입니다.
디바이스들은 그들이 장착된 I2C 버스가 (만약) 없어진다면 자동으로 해제되고
소멸됩니다.
방법 2: 디바이스를 명시적으로 인스턴스로 만들기
———————————————–
이 방법은 좀 더 큰 디바이스가 I2C 버스를 내부 통신 용으로 사용할 때
적절합니다. 일반적인 경우는 TV 어댑터입니다. 이들은 보통 I2C 버스로 메인
칩에 연결되는 튜너, 비디오 디코더, 오디오 디코더, 기타 등을 가질 수
있습니다. 여러분은 미리 I2C 버스의 개수를 알 수 없을텐데, 그래서 위에서
설명한 방법 1 은 사용할 수 없습니다. 대신에 여러분은 여러분의 I2C 디바이스를
명시적으로 인스턴스로 만들 수 있습니다. 이것은 struct i2c_board_info 를
채우고, i2c_new_device() 를 호출함으로써 수행합니다.
예제 (sfe4001 네트워크 드라이버):
static struct i2c_board_info sfe4001_hwmon_info = {
I2C_BOARD_INFO(“max6647”, 0x4e),
};
int sfe4001_init(struct efx_nic *efx)
{
(…)
efx->board_info.hwmon_client =
i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);
(…)
}
위의 코드는 진행 중에 네트워크 어댑터의 I2C 버스 상의 1개의 I2C 디바이스를
인스턴스로 만듭니다.
다른 경우는 I2C 디바이스가 있는지 없는지를 모를 때, 혹은 한 보드로부터
(제조자가 공지없이 그 디자인을 바꾼) 다음 보드로 다른 주소를 가질 때 입니다.
이 경우, 여러분은 i2c_new_device() 대신 i2c_new_probed_device()를 호출할
수 있습니다.
예제 (nxp OHCI 드라이버):
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
static int usb_hcd_nxp_probe(struct platform_device *pdev)
{
(…)
struct i2c_adapter *i2c_adap;
struct i2c_board_info i2c_info;
(…)
i2c_adap = i2c_get_adapter(2);
memset(&i2c_info, 0, sizeof(struct i2c_board_info));
strlcpy(i2c_info.type, “isp1301_nxp”, I2C_NAME_SIZE);
isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
  normal_i2c, NULL);
i2c_put_adapter(i2c_adap);
(…)
}
위의 코드는 진행 중에 OHCI 어댑터에 있는 I2C 버스 상에 1개의 I2C 디바이스를
인스턴스로 만듭니다. 그것은 먼저 0x2c 주소로 시도하고 아무것도 찾지 못하면,
0x2d 주소로 시도하고 나서도 여전히 아무 것도 찾지 못하면, 간단히 포기합니다.
I2C 디바이스를 인스턴스로 만드는 드라이버가 소멸을 위한 청소 작업을
책임집니다. 이는 i2c_new_device() 나 i2c_new_probed_device() 에 의해
반환됐던 포인터 상의 i2c_unregister_device() 를 호출함으로써 수행됩니다.
방법 3: 특정 디바이스를 위한 I2C 버스 감지하기
———————————————-
i2c_new_probed_device() 를 호출한다 할지라도, 어떤 때, 여러분은 I2C
디바이스에 대한 충분한 정보를 갖고 있지 못합니다. 일반적인 경우는 PC 메인보드
상의 하드웨어 모니터링 칩입니다. 25 개의 다른 주소로 장착될 수 있는 많은
모델이 있습니다. 거기다 어마어마한 숫자의 메인보드가 있어, 사용되는 하드웨어
모니터링 칩의 하나도 빠뜨리지 않은 철저한 리스트를 빌드하는 것은 불가능합니다.
운좋게도 이들 대부분의 칩은 제조사와 디바이스 ID 레지스터를 가지기 때문에,
그들은 감지(probing)에 의해서 식별될 수 있습니다.
이런 경우, I2C 디바이스들은 명시적으로 선언되지 않거나, 인스턴스로 되지
않습니다. 대신에, 이런 디바이스를 위한 i2c-core 는 그 드라이버가 로드되자
마자, 어떤 것이 찾아지면, 감지할 것이고, I2C 디바이스는 자동으로 인스턴스로
만들어질 것입니다. 이 메카니즘의 어떠한 오동작도 막기 위해서, 다음 제약 사항이
적용됩니다:
* I2C 디바이스 드라이버는 임의의 레지스터를 읽음으로써 지원되는 디바이스를
  식별할 수 있는 detect() 메소드를 구현해야만 합니다.
* 오직 지원하는 디바이스를 가질 듯하고, 감지를 허용하는 버스들만 감지될
  것입니다. 예를 들면 이것은 TV 어댑터 상의 하드웨어 모니터링 칩을 위한 감지는
  피합니다.
  
예제:
drivers/hwmon/lm90.c 안의 lm90_driver 와 lm90_detect() 를 보세요.
성공적인 감지의 결과로서 인스턴스로 만들어진 I2C 디바이스는 그들을 감지했던
드라이버가 제거될 때, 혹은 아래 깔려있는 I2C 버스 그 스스로가 소멸될 때,
어느 것이 먼저 일어나든지, 자동으로 소멸될 것입니다.
2.4 커널의 i2c 서브 시스템과 초기 2.6 커널에 익숙한 여러분들은 이 방법 3 이
이전에 했던 방법과 기본적으로 비슷한 것을 알 수 있을 것입니다. 두 가지 명확한
차이가 있습니다:
* 감지하는 것은 그 당시에는 I2C 디바이스를 인스턴스로 만드는 유일한 방법이었던
  데 반해, 지금은 그저 하나의 방법일 뿐입니다. 가능한 곳에서는 방법 1 과 2 가
  더 우선되어야 합니다. 방법 3 은 원치 않는 부작용이 있을 수 있기 때문에 다른
  방법이 없을 때에만 사용되어야 합니다.
* 그 당시에는 모든 I2C 버스가 기본값으로 감지되었었던데 반해, I2C 버스들은
  지금은 명시적으로 어떤 I2C 드라이버 클래스가 (그 클래스 비트 필드를 이용해서)
  그들을 감지할 수 있는지 알려줘야만 합니다. 기본값은 아무 감지도 하지 않음을
  의미하는 텅빈 클래스입니다. 그 클래스 비트 필드의 목적은 앞서 언급했던 원치
  않는 부작용을 제한하기 위함입니다.
  
다시 한번, 방법 3은 가능한 곳에서 피하는 것이 좋습니다. 명시적인 디바이스
인스턴스로 만들기(방법 1 과 2)가 더 안전하고, 더 빠른, 훨씬 좋은 방법입니다.
방법 4: 유저-스페이스(user-space)에서 인스턴스로 만들기
——————————————————-
일반적으로, 커널은 어떤 I2C 디바이스가 연결되었고, 어떤 주소를 그들이
사용하는지 알아야만 합니다. 그러나 특정 경우에는 그렇지 않기 때문에
사용자들이 그 정보를 제공하게 하도록 sysfs 인터페이스가 추가되었습니다.
이 인터페이스는 모든 I2C 버스 디렉토리 안에 생성되는 2개의 속성 파일을
생성합니다: new_device 와 delete_device, 두 파일 모두 쓰기 전용이고,
여러분은 그들에 적절하게 I2C 디바이스를 인스턴스를 만들고, 각각 삭제하도록
옳은 파라미터를 써야만 합니다.
new_device 파일은 2 파라미터를 가집니다: 그 I2C 디바이스의 이름(문자열)과
그 I2C 디바이스의 주소(숫자, 일반적으로 0x로 시작하는 16진수, 그러나
10진수로도 표현 가능함)
delete_device 파일은 하나의 파라미터를 가집니다: 그 I2C 디바이스의 주소.
두 디바이스가 주어진 I2C 세그먼트 상에 같은 주소로 장착될 수 없듯이,
그 주소는 삭제하기 위한 디바이스를 유일하게 식별할 수 있는 충분조건입니다.
예제:
# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
이 인터페이스는 커낼 내부(in-kernel) 디바이스 선언이 수행될 수 없을 때에만
사용되어져야 하지만, 유용할 수 있는 여러 경우가 있습니다:
* I2C 드라이버가 일반적으로 디바이스를 감지하지만(방법 3) 여러분의 디바이스가
  장착된 그 버스 세그먼트는 더 적절한 클래스 비트 셋을 가지지 않고, 그래서
  감지가 되지 않을 때.
* I2C 드라이버가 일반적으로 디바이스를 감지하지만, 여러분의 디바이스가 알 수 없는
  주소에 장착될 때.
* I2C 드라이버가 일반적으로 디바이스를 감지하지만, 감지 루틴이 너무 엄격하거나,
  여러분의 디바이스가 공식적으로 아직 지원되지 않기 때문에 여러분의 디바이스가
  감지되지 않지만 여러분은 그와 호환되는 것을 알 때.
* I2C 디바이스를 여러분 스스로 납땜해서 테스트 보드 상에서 드라이버를 개발
  중일 때.
  
이 인터페이스는 몇몇 I2C 드라이버가 구현한 force_* 모듈 파라미터를 대체합니다.
i2c-core 안에 구현된 것이 각 디바이스 드라이버 각자가 구현한 것보다는
훨신 더 효율적이고, 또한 여러분이 셋팅을 바꾸기 위한 드라이버를 다시 로드하지
않아도 된다는 이점을 가집니다. 여러분은 또한 드라이버가 로드되기 전이나 이용
가능하기 전에조차 디바이스를 인스턴스로 만들 수 있고, 그 디바이스가
무슨 드라이버를 필요로 하는지 알 필요도 없습니다.