[t:/]$ 지식_

함수포인터와 점프

2007/07/30

펑션포인터에 직접 주소를 찍고 점프하는 일은 펌웨어 레벨이 아니고서는 거의 쓸 일이 없지만 OS의 소프트웨어 와치독, 커널 패닉시의 소프트웨어 리붓들을 위하여 쓰인다.

소프트웨어 리셋이란 무엇인가?

말 그대로 PC를 그 CPU의 리셋 핸들러 (거의 0x0)으로 점프 시키면 그것이 소프트웨어 리셋이다.

물론, 하이레벨 리붓 프로세싱에는 열린 파일을 닫거나 시스템 로깅을 남긴는 등의 작업이 남아있따.

순수 소프트웨어 리셋이란 그냥 시스템 시작 번지부터 다시 시작 하는 것이다. 리셋 컨트롤러가 정교하게 달린 CPU는 관련 커맨드 레지스터를 가지고 있다.

원래 리셋이 시스템 신뢰성 측면에서 매우 중요한 절차이다. 스트롱암 같은 경우 리셋 커맨드 레지스터가 달려있다.

아마도 리셋은 각종 전원 컨트롤 (서스펜드, RTC 외부전원등..) 과 와치독 과도 깊은 관련이 있으므로 CPU에서 몇 가지를 더 고려하여 처리되고 있을 것으로 추측된다.

또한 부가 장치들의 리셋과도 연계가 되는데 예를 들어 PLL에 의한 외부 장치 공급 클럭에의 씽크 삑사리가 일어날 가능성도 있다. 예컨데 이더넷 칩 재초기화 때 POR이 아닌 리셋으로는 먹통이 되는 현상으로 고생한 적이 있다.

2410X는 PWM 펄스의 듀티비 변경시 변경 순간에 펄스가 망가지지 않고 바로 다음 클럭부터 정수배 컨트롤이 되는 장점이 있으므로, 소프트웨어 리셋시에도 부가 장치에 삑사리 클럭이 들어갈 가능성이 적다고 봐야겠다. (다른 칩도 그런가?? .. 모르겠다..)

소프트웨어 리셋 이야기를 한 것은 펑션포인터에 직접 주소 찍고 PC를 이동하는 루틴에서 삽질을 반복했기 때문이다.

다음을 보자.

void (*run)(void);
run = (void (*)(void))(0x0);
run();

위 코드는 펑션포인터를 직접 주소 0으로 찍어서 시스템을 리부팅 건 것 처럼 보이게 한다. 즉, 소프트웨어 리셋이다.

그러나 이 코드가 잘 동작할때도 있고, 아닐 때도 있었다. 완전히 랜덤한 상황. 이 코드는 부트로더에서는 매우 중요한 코드로서 다음과 같이 사용한다.

  1. POR (Power on reset)
  2. 부트로더가 실행된다.
  3. 부트로더는 NAND 플래시에서 OS Image 를 SDRAM에 복사.
  4. 펑션포인터를 SDRAM의 OS Image 시작번지를 찍고
  5. 그 함수를 실행 -> OS로 점프후 OS 부팅 개시.

그런데 이게 잘 안 되는 것이다. 디테일하게 정확한 이유까지는 모르지만 모든 리눅스 하드웨어 아키코드에서 등장하는 CPU 캐쉬 오프, 플러쉬 함수와 관련이 있는 것 같다.

리눅스 하드웨어 아키코드내에 소프트웨어 리셋 코드를 보면 하나같이 CPU 캐쉬를 오프 시킨후 플러슁(비운다)하고 있다.

이를 보고 짐작되는 이유는. (여기서 부터 추측.)

메모리상에 연속적으로 또는, 정해진 점프계통 명령어에 의해서 연속적이라고 읽혀지는 인스트럭션은 캐쉬에 의해서 "예측될 수 있다" 라는 것이다.

여기에 파이프라인 기술까지 들어가면. 다음다음 인스트럭션까지도 프리패치를 시도하고 있다고 봐야 한다.

그러나 이렇게 펑션포인터에서 임의로 상수값으로 정의된 번지 (그 번지에는 뭐가 있는지 CPU 캐쉬는 전혀 예측할 수 없다. 이미 부트로더와 같은 프로그램에 의해서 새로운 프로그램이 업데이트 되든가 하는 상황이 발생하고 있기 때문) 로 점프해 버리면 CPU 인스트럭션 캐쉬는 힛팅을 못시켜서 효율이 떨어진 것이 아니라 오히려 힛팅이라고 예상하고 패치시켜버린 인스트럭션이 실행되어버리고 다음 인스트럭션들이 펑션포인터가 찍어논 위치에서 줄줄이 따라 들어오므로 전혀 예측이 불가능한 상황으로 치달아 시스템이 정지해버린다.

따라서 펑션 포인터에 직접 상수값 번지를 찍어서 PC를 이동시킬때에는 캐쉬를 끄고, 비우고, 이동 시킨후 이동 시킨 코드에서는 다시 캐쉬를 켜야 한다. (캐쉬를 끄면 LED 깜빡소스 조차도 굼벵이가 된다)

실제로 DS의 테스트 코드에서 MMU를 위한 캐쉬 플러슁 코드를 업어다가 조립했더니 이제는 잘 동작하고 있다.

우리 타겟 제품들과 연관하면. 고 신뢰성을 위해서 자가수복기능들. 예를 들어 와치독과 같은 재부팅, 다양한 크래쉬 상황에서의 재부팅, 펌웨어 업그레이드 후 자동 재부팅 또는 근미래를 내다보면 커널 업그레이드 후 재부팅 없이 커널 재실행을 위해서, 고신뢰성 달성을 위해서 필요한 기능이다.

어드레스 상수값의 연산시 긴 값을 사용하므로 상수값 뒤에 L 을 붙여서 자동캐스팅에 의한 값 손실이 없도록 유의한다.

예 ) 0x30000000 - 4 = ???

이런경우 컴파일러에 따라 하위 워드만 계산되고 상위워드는 계산되지 않을 수도 있다는 점!





공유하기













[t:/] is not "technology - root". dawnsea, rss