메모리 어보트 타입 익셉션이 발생하면 익셉션 핸들러에서 디버깅 정보를 출력하고 시스템을 리셋시킵니다. 소프트웨어적으로 시스템이 치명적인 오류가 있는 상태로 판단하기 때문입니다.

다음 그림을 보면서 메모리 어보트 타입 익셉션의 전체 실행 흐름을 알아봅시다.  


그림 8.2 메모리 어보트 타입 익셉션이 발생할 때 전체 흐름

익셉션의 전체 실행 흐름도는 4단계로 분류할 수 있는데, 각 단계 별로 어떤 동작을 수행하는지 알아봅시다. 



1단계: 프로세스가 실행하는 도중에 익셉션을 유발하는 명령어 실행 

먼저 ①로 표시된 부분을 눈으로 따라가 봅시다. 프로세스가 메모리 어보트를 유발하는 명령어를 실행하는 부분입니다.



2단계: ARM 프로세서가 익셉션을 감지 

② 로 표시된 부분은 ARM 프로세서가 익셉션을 감지하는 동작입니다. 1단계에서 메모리 어보트를 유발하는 명령어를 ARM 코어가 실행하면 다음과 같은 익셉션을 유발합니다.  

    ❑ Undefined Instruction
    ❑ 프리패치 어보트
    ❑ 데이터 어보트

②~③으로 표기된 부분의 외곽에 보이는 테두리는 ARM 프로세서가 하드웨어적으로 처리되는 부분입니다. 이 동작은 소스 코드 형태로 확인하기 어렵습니다.



3단계: ARM 프로세서가 익셉션에 대한 세부 처리 

③으로 표기된 박스를 보겠습니다. 익셉션을 감지한 ARM 코어는 다음과 같은 동작을 처리합니다.

    ❑ 익셉션이 발생한 시점의 ARM 동작 모드를 나타내는 CPSR 레지스터를 spsr_<mode> 레지스터에 저장한다. 
    ❑ CPSR 레지스터를 변경해 ARM의 동작 모드를 변경한다.
    ❑ 익셉션의 종류에 따라 이미 지정된 주소로 프로그램 카운터를 브랜치한다.

많은 SW 개발자들은 위 동작을 보고 "소스 코드로 보고 싶다"란 생각이 들지도 모르겠지만, 이 동작은 "하드웨어적으로" ARM 프로세서가 처리되는 부분입니다.  



4단계: 익셉션 핸들러 실행 후 시스템 콜 핸들러 호출


④으로 표기된 박스는 SW적으로 처리되는 부분입니다. 3단계에서 익셉션의 종류에 따라 이미 지정된 주소로 프로그램 카운터를 브랜치하면, 익셉션의 종류에 따라 이미 지정된 주소에 위치한 명령어가 실행됩니다. 익셉션의 종류에 따라 이미 지정된 주소를 익셉션 벡터 주소라고 하며, 익셉션의 종류에 따라 이미 지정된 주소에 위치한 코드를 익셉션 벡터 핸들러라고 합니다. 익셉션 벡터로 구성된 익셉션 벡터 테이블은 다음 절에서 자세히 다룰 예정입니다.

 

유튜브 강의 영상

 
 
 

 

 

 

처음에 ARM 아키텍처의 익셉션을 공부할 때 익셉션 벡터 테이블을 이해하는데만 집중하는 분들이 많습니다. "ARMv7 아키텍처의 익셉션 동작 원리를 파악하려면 익셉션 벡터 테이블만 제대로 이해하면 된다"라고 생각하기 때문입니다. 하지만 익셉션 벡터 테이블의 내용만 익히면 배운 내용을 실전 프로젝트에 활용하기 어렵습니다.

왜냐면, 익셉션 벡터 테이블의 내용보다도 다음과 같은 사실을 파악하는게 더 중요하기 때문입니다.

    ❑ 익셉션은 소프웨어적으로 어떤 명령어가 실행될 때 유발될까?
    ❑ ARM 프로세서가 익셉션을 감지한 후 소프트웨어적으로 어떤 처리를 수행할까?

위에서 소개한 질문에 답을 하려면 다음 그림에서 소개한 익셉션을 구성하는 주요 개념에 대해 파악할 필요가 있습니다. 


 
그림 8.1 ARMv7 익셉션을 구성하는 주요 개념

그림 8.1에서 먼저 ‘유발 요인’으로 표기된 원의 내용부터 살펴봅시다.

먼저 그림을 보면 가운데 부분에 선이 보입니다. 이 선의 윗 부분은 ARM 코어가 익셉션을 감지하면 이를 하드웨어적으로 처리하는 과정을 나타내고, 아랫 부분은 소프트웨어적으로 익셉션을 처리하는 부분을 나타냅니다.

 

유발요인

익셉션이 동작하는 원리의 전체 맥락과 흐름을 파악하려면 익셉션을 유발하는 원인을 구체적으로 파악해야 합니다. 먼저 그림의 왼쪽 아랫 부분에 있는 원은 익셉션을 유발하는 요인을 나타내는데, 그 항목은 다음과 같습니다. 

    ❑ 메모리 어보트: 데이터 어보트(Data Abort), 프리페치 어보트(Prefetch Abort), Undefined Instruction 
    ❑ 외부 인터럽트(IRQ/FIQ)
    ❑ SW 인터럽트

익셉션을 유발하는 첫 번째 요인은 메모리 어보트를 유발하는 명령어를 실행했을 때입니다. ARM 코어는 명령어를 세부 단계로 나눠서 처리하는데, 이 과정에서 오류를 확인하면 데이터 어보트(Data Abort), 프리페치 어보트(Prefetch Abort), Undefined Instructiond와 같은 메모리 어보트 타입 익셉션을 유발합니다.

익셉션을 유발하는 두 번째 요인은 외부 하드웨어에서 전달되는 인터럽트입니다. ARM 코어가 외부 하드웨어에서 IRQ 인터럽트나 FIQ(Fast Interrupt Request) 인터럽트를 감지하면 익셉션을 유발합니다.

 


[정보]
여기서 말하는 외부 하드웨어는 컴퓨터에서 사용하는 키보드나 마우스 그리고 휴대폰의 센서와 같은 디바이스를 의미합니다.

마지막 익셉션의 유발 요인은 소프트웨어 인터럽트입니다. 용어 그대로 소프트웨어으로 유발되는 인터럽트인데, 'svc' 명령어를 실행하면 ARM 프로세서는 익셉션을 유발합니다. 

IRQ와 같은 인터럽트는 외부 하드웨어에서 비동기적으로 인터럽트가 발생할 때 유발되는 익셉션인데, 소프트웨어적으로 'svc' 명령어를 실행하면 ARM 코어는 이를 '인터럽트' 익셉션을 유발하므로 이를 소프트웨어 인터럽트라고도 부릅니다.

 

운영체제에서 유저 애플리케이션이 실행되는 User 모드에서 'svc' 명령어를 실행해 커널 함수가 실행되는 슈퍼바이저 모드로 진입합니다. 이 과정을 운영체제에서는 시스템 콜이라고 부릅니다.

 


레지스터 업데이트

ARM 코어는 익셉션을 감지하면 특정 ARM의 작동 모드(Operation Mode)에서만 접근하는 레지스터를 다음과 같이 변경합니다. 

    ❑ 익셉션이 발생한 시점의 CPSR 레지스터를 변경되는 모드의 spsr_<mode> 레지스터에 백업
    ❑ 익셉션이 발생한 순간에 실행된 주소 기준으로, 익셉션이 유발된 다음 명령어로 복귀할 주소를 
       R14_<mode> 레지스터에 백업

이와 같이 익셉션이 발생한 순간의 정보를 레지스터에 저장하는 이유는, 익셉션의 타입에 따라 익셉션이 발생하기 직전의 모드와 주소로 복귀하는 동작을 지원하기 위해서입니다. 

 

ARM 동작 모드 변경

'ARM의 동작 모드'는 익셉션을 구성하는 주요 개념 중 하나입니다. ARM 코어에서 익셉션을 유발하면 익셉션에 대응되는 ARM의 동작 모드(슈퍼바이저 모드, IRQ 모드)가 변경되므로 'ARM의 동작 모드'에 대해서도 잘 알고 있어야 합니다.  

ARM 코어는 익셉션의 종류를 감지한 다음, 익셉션의 종류 별로 처리하는 동작 모드를 변경하는데, 익셉션의 종류 별로 처리되는 동작 모드의 목록은 다음 표와 같습니다.



ARM 코어는 CPSR 레지스터의 [4:0] 비트를 업데이트해, 익셉션의 종류 별로 처리할 ARM의 동작 모드를 변경합니다. 각 익셉션의 종류 별로 변경되는 각각의 모드가 있는데, 프리패치 어보트와, 데이터 어보트와 같은 익셉션을 감지하면 공통으로 '어보트 모드'로 변경합니다. 

ARM 코어는 익셉션의 종류 별로 ARM 모드를 지정해 처리하는데, 익셉션의 종류에 따른 실행 흐름 구분해 처리할 수 있습니다.

 

익셉션 벡터 테이블

익셉션 벡터 테이블을 설명하기 전에 먼저 관련 용어를 먼저 소개하겠습니다. 먼저 익셉션 벡터 테이블에 대해 알아봅시다. 익셉션 벡터 테이블은 용어 그대로 익셉션 벡터로 구성된 테이블이라 볼 수 있습니다. 

그렇다면 익셉션 벡터란 무엇일까요? ARM 코어는 익셉션을 감지해 익셉션의 종류를 식별한 다음에, 익셉션의 종류 별로 지정된 주소로 프로그램 카운터를 브랜치합니다. 쉽게 설명하면, 프로그램 카운터에 익셉션의 종류 별로 지정된 주소를 넣어주는 동작입니다. 여기서 말하는 익셉션의 종류 별로 지정된 주소를 익셉션 벡터라고 합니다.

익셉션 벡터 테이블은 익셉션 벡터로 구성된 테이블인데, 2가지 내용으로 구성돼 있습니다.

    ❑ 익셉션의 종류
    ❑ 익셉션의 종류 별 오프셋 주소

익셉션이 발생하면 ARM 아키텍처에서 정한 규칙에 따라 익셉션 벡터로 프로그램 카운터가 브랜치되는데, 이 내용을 익셉션 벡터 테이블이 담고 있습니다.

익셉션 벡터와 익셉션 벡터 테이블이란 용어를 같이 사용해 익셉션의 동작 원리를 다음과 같이 설명할 수 있습니다.

    “익셉션이 발생하면 익셉션 벡터 테이블에 명시된 익셉션 벡터로 
     프로그램 카운터가 브랜치한다”

이어서 ARM 스팩 문서를 보면서, 익셉션이 유발될 때 ARM 프로세서는 어떤 방식으로 익셉션 벡터를 프로그램 카운터로 브랜치하는지 알아봅시다. ARM 사에서 배포한 스팩 문서 'DDI0403E_d_armv7m_arm'를 보면 익셉션이 발생하면 익셉션 벡터로 프로그램 카운터를 브랜치하는 동작을 다음과 같은 슈도 코드로 표기합니다.

// Branch to Undefined Instruction vector.
BranchTo(ExcVectorBase() + vect_offset);

위 코드에서 ExcVectorBase() 함수는 익셉션 벡터 테이블의 베이스 주소, vect_offset는 익셉션 벡터 테이블 주소 기준의 오프셋을 의미합니다. 'ExcVectorBase() + vect_offset' 구문은 익셉션 벡터 테이블의 베이스 주소에서 오프셋을 더한 주소인데, 이 인자를 적용해 BranchTo() 함수를 호출합니다. BranchTo() 함수는 인자를 프로그램 카운터로 브랜치하는 동작을 나타내는 슈도 코드 함수입니다. 


정리하면, 위 슈도 코드는 다음과 같은 동작을 나타냅니다. 
   
    “익셉션이 발생하면 익셉션 벡터로 프로그램 카운터를 브랜치한다”

이제 앞으로 계속 사용될 익셉션 벡터 테이블과 관련된 용어를 간단히 정리해봅시다. 

    ❑ 익셉션 벡터: 익셉션의 종류 별로 ARM 프로세서가 프로그램 카운터로 브랜치하는 주소
    ❑ 익셉션 벡터 테이블: 익셉션 벡터로 구성된 일종의 테이블
    ❑ 익셉션 벡터 베이스 주소: 익셉션 벡터 테이블의 시작 주소를 의미하면 익셉션 벡터 주소를 정할 때의
        기준이 되는 주소

익셉션 벡터 테이블의 자세한 내용은 다음 포스트에서 살펴볼 예정입니다. 

이어서 익셉션 벡터로 프로그램 카운터를 이동하면 익셉션 벡터 주소에 위치한 익셉션 핸들러가 처리되는 과정을 살펴봅시다.

익셉션 핸들러

ARM 코어가 익셉션 벡터 주소로 프로그램 카운터를 브랜치하면, 익셉션 벡터 주소에 위치한 명령어가 실행됩니다. 이 때 익셉션 핸들러가 실행되는데, 익셉션의 종류에 따라 각기 다른 방식으로 처리합니다.

    ❑ 메모리 어보트 타입 익셉션: 시스템 리셋
    ❑ IRQ 익셉션: 인터럽트를 처리하는 인터럽트 서비스 루틴 실행
    ❑ SW 인터럽트 익셉션: 시스템 콜 핸들러 실행

첫 번째로, 프리패치 어보트, 데이터 어보트 그리고 Undefined Instruction과 같은 메모리 어보트 타입 익셉션의 경우, 해당 익셉션 핸들러에서 레지스터와 같은 디버깅 정보를 출력합니다. 메모리 어보트 타입 익셉션은 소프트웨어적으로 치명적인 오류가 있을 때 유발되는데, 프로그램의 유형에 따라 달리 처리합니다.

    ❑ 유저 애플리케이션: 프로세스를 종료
    ❑ 운영체제의 커널이나 커널 드라이버: 시스템을 리셋

[정보]
리눅스 운영체제에서 커널 패닉이나 커널 크래시가 발생할 경우 메모리 어보트 타입 익셉션이 유발됩니다. 

둘째, IRQ 익셉션은 메모리 어보트 타입 익셉션과 처리 방식이 다릅니다. IRQ 익셉션은 외부 하드웨어와 소프트웨어 간의 인터페이스와 같은 기능이므로, IRQ 익셉션이 발생하면 실행되는 익셉션 핸들러에서 인터럽트 서비스 루틴을 실행합니다. 인터럽트는 하드웨어의 변화를 알리기 위한 인터페이스로 동작하므로, 인터럽트 서비스 루틴에서 인터럽트 핸들러이 호출되며, 인터럽트 핸들러에서 인터럽트에 대한 처리를 수행합니다.

셋째, 'svc'과 같은 명령어를 실행해 처리되는 소프트웨어 인터럽트의 경우, 해당 익셉션 핸들러에서 시스템 콜 핸들러로 분기하는 동작을 수행합니다. 운영체제 커널에서는 시스템 콜 핸들러의 정보를 포함하는 시스템 콜 테이블이 존재하는데, 시스템 콜 테이블의 정보를 참고해 시스템 콜 핸들러가 호출됩니다. 소프트웨어 인터럽트 익셉션도 IRQ 익셉션과 마찬가지로 운영체제의 시스템 콜을 지원하는 기능으로 동작합니다.

여기까지 익셉션을 이루는 주요 기능에 대해 살펴봤습니다. 여러분이 익셉션과 관련된 코드나 스팩 문서를 볼 때 이번 절에 소개된 내용을 머릿 속으로 떠올리면 전체 실행 흐름을 더 쉽게 이해할 수 있습니다. 

 

유튜브 강의 영상

 
 

 

 

 

 

<< 리눅스 커널 및 리눅스 시스템 Track>>

리눅스 커널의 동작 원리 (저자 직강 방식)

 - 리눅스 커널의 주요 디버깅 툴
 - 프로세스, 태스크 스케줄링 
 - 인터럽트 및 인터럽트 후반부
 - Soft IRQ, 워크큐
 - 커널 동기화, 배리어
 - 시그널, 시스템 콜
 - 가상 파일 시스템, 주요 파일 시스템
 - 매모리 매니지먼트

리눅스 디바이스 드라이버 Overview 

- 리눅스 디바이스 드라이버의 전체 구조
- 디바이스 드라이버 디버깅 피쳐
- 모듈 디바이스 드라이버 기본 구조
- 캐릭터 디바이스 드라이버
- 블락 디바이스 드라이버
- 플렛폼 디바이스 드라이버
- 디바이스 트리 및 최적화
- 인터럽트 핸들링, Top-half와 Bottom Half
- 디바이스 드라이버 동기화 이슈 

리눅스 시스템 리소스 모니터링  

- 리눅스 OS 와 프로세스 이해
- 리눅스 SW 동작원리
- 리눅스 메모리(Stack / Heap) 분석과 이해
- 리눅스 OS 이해와 커널함수 추적
- 리눅스 파일 Read / Write 처리과정
- 리눅스 시스템 트러블슈팅


<< Arm 프로세서 Track >>

Arm 프로세서(64비트 Armv8-A 아키텍처) 동작 원리 (저자 직강 방식)

- 레지스터(Register)
- 어셈블리 명령어
- AAPCS(함수 호출 규약)
- 익셉션 레벨(Exception Level)
- 익셉션(Exception)
- 크래시 폴트 핸들링 매커니즘
- GIC(Generic Interrupt Controller)
- 트러스트존(Trustzone)
- 가상화(Virualization)
- 메모리 배리어와 캐시
- MMU(Memory Management)

Arm 프로세서(32비트 Armv7-A 아키텍처) 동작 원리 (저자 직강 방식)

- 레지스터(Register)
- 어셈블리 명령어
- AAPCS(함수 호출 규약)
- 동작 모드 
- 익셉션(Exception)
- 크래시 폴트 핸들링 매커니즘
- GIC(Generic Interrupt Controller)
- 트러스트존(Trustzone)
- 가상화(Virualization)
- 메모리 배리어와 캐시
- MMU(Memory Management)

 


리눅스 커널과 Arm 프로세서 인터페이스 (저자 직강 방식)

- 프로세스의 스택과 함수 호출 규약
- 익셉션 레벨, 동작 모드와 커널 공간
- 컨텍스트의 의미와 레지스터
- 컨텍스트 스위칭
- 시스템 콜, 시그널
- 스핀락, 배리어
- 페이지 폴트, 페이지 테이블 
- 트러스트존 드라이버

<< 크래시 덤프 분석 및 트러블슈팅 Track >>

TRACE32와 crash-utility 로 크래시 덤프 분석으로 리눅스 커널 분석하기

 - 주요 리눅스 커널 구조 디버깅(프로세스, 태스크 스케줄링, 메모리, 파일 시스템) 
 - 깨진 콜 스택 복원
 - CMM 스크립트 작성으로 커널 자료 구조를 파싱 
 - Crash-Utility로 주요 메모리 정보 프로파일링 
 - 예제 크래시 덤프 분석 (락업, 메모리 오염, BUG, 패닉)

 

트러블슈팅 - 주요 예제 덤프 분석(TRACE32, crash-utility 사용)

 - 예제 크래시 덤프 분석 (락업, 메모리 오염, BUG, 패닉)
 - ftrace 메시지 분석하기
 - ftrace 메시지와 메모리 덤프로 주요 리눅스 커널 구조 디버깅 
 - 깨진 콜 스택 복원
 - 디버깅 패치 작성 방법

When compiling crash utility in the Raspbian with Aarch32,
do not use 'j' option like (make -j5).

apt update
apt-get install git build-essential bison zlib1g-dev libncurses5-dev libncursesw5-dev  pkg-config flex swig -y
apt-get install -y texinfo
cd crash
make target=ARM

<From: https://m.blog.naver.com/lithium81/80137729050>

gzip의 분할 압축 명령

$ tar zcvfp - 대상파일(들) | split -b 4m - 압축할이름.tar.gz [엔터] 또는,
$ tar zcvfp - 디렉토리/ | split -b 4m - 압축할이름.tar.gz [엔터]

실행하면 *.tar.gzaa, ....gzab, ....gzac 이런 식으로 파일 확장자의 문자열이 증가하면서 파일이 생성된다.

gzip의 분할된 압축 파일 하나로 합치기

$ cat 압축파일이름.tar.gza* > 합쳐진파일이름.tar.gz [엔터]

실행하면 통짜로 된 .tar.gz 파일만 얻을 수 있다.

(참고: tar + gzip 압축 파일 해제)
~$ tar zxvf 압축파일이름.tar.gz [엔터]

실행하면 압축했던 디렉토리를 복구할 수 있다.

 

// example

 

cat panic.tar.gza* > a_panic.tar.gz
gzip -d  a_panic.tar.gz
tar -zvxf a_panic.tar
tar -xvf a_panic.tar

'current' macro is mainly used to read 'task_struct' address of current process.

arch/riscv/include/asm/current.h
register struct task_struct *riscv_current_is_tp __asm__("tp");

static __always_inline struct task_struct *get_current(void)
{
return riscv_current_is_tp;
}

#define current get_current()

Body of current is 'get_current()' macro. We can deduce that "tp"(X4) holds start address of current.

Core routine of context switching in RISC-V Linux kernel is below.

ENTRY(__switch_to)
/* Save context into prev->thread */
li    a4,  TASK_THREAD_RA
add   a3, a0, a4
add   a4, a1, a4
REG_S ra,  TASK_THREAD_RA_RA(a3)
REG_S sp,  TASK_THREAD_SP_RA(a3)
REG_S s0,  TASK_THREAD_S0_RA(a3)
REG_S s1,  TASK_THREAD_S1_RA(a3)
REG_S s2,  TASK_THREAD_S2_RA(a3)
REG_S s3,  TASK_THREAD_S3_RA(a3)
REG_S s4,  TASK_THREAD_S4_RA(a3)
REG_S s5,  TASK_THREAD_S5_RA(a3)
REG_S s6,  TASK_THREAD_S6_RA(a3)
REG_S s7,  TASK_THREAD_S7_RA(a3)
REG_S s8,  TASK_THREAD_S8_RA(a3)
REG_S s9,  TASK_THREAD_S9_RA(a3)
REG_S s10, TASK_THREAD_S10_RA(a3)
REG_S s11, TASK_THREAD_S11_RA(a3)
/* Restore context from next->thread */
REG_L ra,  TASK_THREAD_RA_RA(a4)
REG_L sp,  TASK_THREAD_SP_RA(a4)
REG_L s0,  TASK_THREAD_S0_RA(a4)
REG_L s1,  TASK_THREAD_S1_RA(a4)
REG_L s2,  TASK_THREAD_S2_RA(a4)
REG_L s3,  TASK_THREAD_S3_RA(a4)
REG_L s4,  TASK_THREAD_S4_RA(a4)
REG_L s5,  TASK_THREAD_S5_RA(a4)
REG_L s6,  TASK_THREAD_S6_RA(a4)
REG_L s7,  TASK_THREAD_S7_RA(a4)
REG_L s8,  TASK_THREAD_S8_RA(a4)
REG_L s9,  TASK_THREAD_S9_RA(a4)
REG_L s10, TASK_THREAD_S10_RA(a4)
REG_L s11, TASK_THREAD_S11_RA(a4)
/* Swap the CPU entry around. */
lw a3, TASK_TI_CPU(a0)
lw a4, TASK_TI_CPU(a1)
sw a3, TASK_TI_CPU(a1)
sw a4, TASK_TI_CPU(a0)
/* The offset of thread_info in task_struct is zero. */
move tp, a1
ret
ENDPROC(__switch_to)
   

'move tp, a1' instruction corresponding to 'move X4, X11' is added to save the starting address of 'task_struct.

Body of 'alloc_fd' function.

fs/file.c
static int alloc_fd(unsigned start, unsigned end, unsigned flags)
{
struct files_struct *files = current->files;

The 'current' macro is used as a way to read starting address of 'task_struct'.

SP:FFFFFFFF8014E5B0|711D      alloc_fd: c.addi16sp -0x60         ; -96
SP:FFFFFFFF8014E5B2|E8A2                c.sdsp     x8,0x50(x2)   ; x8,80(x2)
SP:FFFFFFFF8014E5B4|F852                c.sdsp     x20,0x30(x2)   ; x20,48(x2)
SP:FFFFFFFF8014E5B6|F456                c.sdsp     x21,0x28(x2)   ; x21,40(x2)
SP:FFFFFFFF8014E5B8|F05A                c.sdsp     x22,0x20(x2)   ; x22,32(x2)
SP:FFFFFFFF8014E5BA|EC5E                c.sdsp     x23,0x18(x2)   ; x23,24(x2)
SP:FFFFFFFF8014E5BC|E862                c.sdsp     x24,0x10(x2)   ; x24,16(x2)
SP:FFFFFFFF8014E5BE|EC86                c.sdsp     x1,0x58(x2)   ; x1,88(x2)
SP:FFFFFFFF8014E5C0|E4A6                c.sdsp     x9,0x48(x2)   ; x9,72(x2)
SP:FFFFFFFF8014E5C2|E0CA                c.sdsp     x18,0x40(x2)   ; x18,64(x2)
SP:FFFFFFFF8014E5C4|FC4E                c.sdsp     x19,0x38(x2)   ; x19,56(x2)
SP:FFFFFFFF8014E5C6|E466                c.sdsp     x25,0x8(x2)   ; x25,8(x2)
SP:FFFFFFFF8014E5C8|1080                c.addi4spn x8,0x60       ; x8,96
SP:FFFFFFFF8014E5CA|67823903            ld         x18,0x678(x4)   ; x18,1656(x4)

After 'ld x18,0x678(x4)' instruction is executed, x18 is holding the &(current->files) where 0x678 is offset of 'struct task_struct.files.
This is true because x4 holds the entry address of 'task_struct'.

fs/file.c
int __close_range(unsigned fd, unsigned max_fd, unsigned int flags)
{
struct task_struct *me = current;
struct files_struct *cur_fds = me->files, *fds = NULL;
 
      SP:FFFFFFFF8014EC14|__close_range:    c.addi16sp -0x60         ; -96
      SP:FFFFFFFF8014EC16|                  c.sdsp     x8,0x50(x2)   ; x8,80(x2)
      SP:FFFFFFFF8014EC18|                  c.sdsp     x1,0x58(x2)   ; x1,88(x2)
      SP:FFFFFFFF8014EC1A|                  c.sdsp     x21,0x28(x2)   ; x21,40(x2)
      SP:FFFFFFFF8014EC1C|                  c.addi4spn x8,0x60       ; x8,96
      SP:FFFFFFFF8014EC1E|                  ld         x15,0x460(x4)   ; x15,1120(x4)
      SP:FFFFFFFF8014EC22|                  sd         x15,-0x58(x8)   ; x15,-88(x8)
      SP:FFFFFFFF8014EC26|                  c.li       x15,0x0       ; x15,0

From:
https://msyksphinz-self.github.io/riscv-isadoc/html/index.html

...
MP:FFFFFFFF80002FEA|                    csrrc      x9,sstatus,x5  // atomic read and clear bits in CSR.
MP:FFFFFFFF80002FEE|                    csrr       x18,sepc
MP:FFFFFFFF80002FF2|                    csrr       x19,stval
MP:FFFFFFFF80002FF6|                    csrr       x20,scause
MP:FFFFFFFF80002FFA|                    csrr       x21,sscratch

void handle_exception() 
{
   word scause;
   word offset_vect;
   void *exception_func;
   bool interrupt_status;

   scause = sys_csr_reg_read(scause); // csrr       x20,scause

   interrupt_status = scause >> (SXLEN -1); // 

   if ( interrupt_status == true) {
       la_reg = ret_from_exception;  // la ra, ret_from_exception
       handle_arch_irq(); 
   }
   else {
      la_reg = ret_from_exception;  // la ra, ret_from_exception
      if (scause & 0b1000) {
         handle_syscall();
      }
      else {
         offset_vect = get_exception_reason(scause);
         exception_func = excp_vect_table + offset_vect;
         (void*)exception_func; // c.jr x5
      }
   }
}

From: https://doc-en.rvspace.org/VisionFive2/SWTRM/VisionFive2_SW_TRM/swtrm_compiling_linux_kernel%20-%20vf2.html

 

# good command to start 'crash utility'

 

1. 1st verified command 


./crash dump1@0x80000000,dump2@0x880000000 -p 4096 -m vabits_actual=39 -m kimage_voffset=0xffffffbf70000000 --no_panic --smp $1

 

2. Under progress

 

./crash1 -d -6 SYS_COREDUMP -m --kaslr=0x1d43c00000 -m vabits_actual=39 -m kimage_voffset=0xffffffa611c00000 -m --smp vmlinux

diff --git a/arm64.c b/arm64.c
index bdb2a0a..7c3a8ea 100644
--- a/arm64.c
+++ b/arm64.c
@@ -361,6 +361,19 @@ arm64_init(int when)
        /* use machdep parameters */
        arm64_calc_phys_offset();
     
+       error(INFO, "[+][%s][%d] at %s\n", __func__, __LINE__, __FILE__);
+       error(INFO, "kimage_voffset: %lx phys_offset: %lx \n",
+                               machdep->machspec->kimage_voffset, machdep->machspec->phys_offset);
+
+        error(INFO, "CONFIG_ARM64_VA_BITS: %ld\n", ms->CONFIG_ARM64_VA_BITS);
+        error(INFO,  "     VA_BITS_ACTUAL: %ld\n", ms->VA_BITS_ACTUAL);
+        error(INFO, "(calculated) VA_BITS: %ld\n", ms->VA_BITS);
+        error(INFO, " PAGE_OFFSET: %lx\n", ARM64_PAGE_OFFSET_ACTUAL);
+        error(INFO, "    VA_START: %lx\n", ms->VA_START);
+        error(INFO, "     modules: %lx - %lx\n", ms->modules_vaddr, ms->modules_end);
+        error(INFO, "     vmalloc: %lx - %lx\n", ms->vmalloc_start_addr, ms->vmalloc_end);
+        error(INFO, "kernel image: %lx - %lx\n", ms->kimage_text, ms->kimage_end);
+        error(INFO, "     vmemmap: %lx - %lx\n\n", ms->vmemmap_vaddr, ms->vmemmap_end);
        if (CRASHDEBUG(1)) {
            if (machdep->flags & NEW_VMEMMAP)
                fprintf(fp, "kimage_voffset: %lx\n",
@@ -816,6 +829,11 @@ arm64_parse_cmdline_args(void)
                    "setting max_physmem_bits to: %ld\n\n",
                    machdep->max_physmem_bits);
                continue;
+           } else if (arm64_parse_machdep_arg_l(arglist[i], "kaddr_offset",
+               &machdep->machspec->kimage_addr_offset)) {
+               error(WARNING, "setting kimage_addr_offset to: 0x%lx\n\n",
+                                   machdep->machspec->kimage_addr_offset);
+               continue;
            }
  
            error(WARNING, "ignoring --machdep option: %s\n",
@@ -1011,6 +1029,12 @@ arm64_kdump_phys_base(ulong *phys_offset)
    if ((machdep->flags & NEW_VMEMMAP) &&
        machdep->machspec->kimage_voffset &&
        (sp = kernel_symbol_search("memstart_addr"))) {
+
+       physaddr_t kimage_offset_addr = machdep->machspec->kimage_addr_offset;
+
+       if (kimage_offset_addr) {
+           machdep->machspec->kimage_voffset -= kimage_offset_addr;
+       }
        paddr = sp->value - machdep->machspec->kimage_voffset;
        if (READMEM(-1, phys_offset, sizeof(*phys_offset),
            sp->value, paddr) > 0)
diff --git a/defs.h b/defs.h
index 42e381e..a377611 100644
--- a/defs.h
+++ b/defs.h
@@ -3259,6 +3259,7 @@ struct machine_specific {
    ulong VA_BITS_ACTUAL;
    ulong CONFIG_ARM64_VA_BITS;
    ulong VA_START;
+   ulong kimage_addr_offset;
 };
  
 struct arm64_stackframe {
diff --git a/main.c b/main.c
index 83ccd31..971486e 100644
--- a/main.c
+++ b/main.c
@@ -21,6 +21,13 @@
 #include <getopt.h>
 #include <sys/prctl.h>
  
+#define OCIMEM_OFFSET_ADDR             0x14680000
+#define RPI5_KASLR_ADDR_LOCATION     0x146bf6d0
+#define ADDR_READ_OFFSET           0x4
+
+#define RPI5_ADDR_FORMAT_SIZE    19
+#define RPI5_KASLR_ADDR_OFFSET  ( RPI5_KASLR_ADDR_LOCATION - OCIMEM_OFFSET_ADDR + ADDR_READ_OFFSET )
+
 static void setup_environment(int, char **);
 static int is_external_command(void);
 static int is_builtin_command(void);
@@ -80,6 +87,9 @@ main(int argc, char **argv)
 {
    int i, c, option_index;
    char *tmpname;
+   FILE *ocimem_file = NULL;
+   uint64_t c_64, kaslr_offset = 0;
+   char *optarg_backup;
  
    setup_environment(argc, argv);
  
@@ -226,6 +236,29 @@ main(int argc, char **argv)
                        optarg);
                }
            } else if (STREQ(long_options[option_index].name, "kaslr")) {
+               ocimem_file = fopen("./OCIMEM.BIN", "r");
+
+               if(!ocimem_file) {
+                   error(INFO, "Fail to open OCIMEM.BIN\n");
+                   goto OCIMEM_READ_EXIT;
+               }
+
+               fseek(ocimem_file, RPI5_KASLR_ADDR_OFFSET, SEEK_SET);
+
+               optarg_backup = optarg;
+               optarg = malloc(RPI5_ADDR_FORMAT_SIZE);
+
+               for(i=0;i<sizeof(uint64_t);i++) {
+                   c_64 = getc(ocimem_file);
+                   kaslr_offset |= (c_64 << (i*8));
+               }
+              
+               snprintf(optarg, RPI5_ADDR_FORMAT_SIZE, "0x%lx", kaslr_offset);
+               error(INFO, "kaslr_offset=%s\n", optarg);
+OCIMEM_READ_EXIT:
+               if(ocimem_file)
+                   fclose(ocimem_file);
+
                if (!machine_type("X86_64") &&
                    !machine_type("ARM64") && !machine_type("X86") &&
                    !machine_type("S390X"))

 

>>>

* bad dump

SECTIONS_PER_ROOT = 128
SECTION_ROOT_MASK = 0x7f
PAGES_PER_SECTION = 262144
<readmem: ffffffa653a30058, KVADDR, "mem_section", 8, (FOE), 7ffd4f76eb20>
<read_kdump: addr: ffffffa653a30058 paddr: 41e30058 cnt: 8>
read_netdump: addr: ffffffa653a30058 paddr: 41e30058 cnt: 8 offset: 1e34058
<readmem: 0, KVADDR, "memory section root table", 64, (FOE), 5625b4edac70>
crash1: invalid kernel virtual address: 0  type: "memory section root table"


        if (!(vt->mem_sec = (void *)malloc(mem_section_size)))
                error(FATAL, "cannot malloc mem_sec cache\n");
        if (!(vt->mem_section = (char *)malloc(SIZE(mem_section))))
                error(FATAL, "cannot malloc mem_section cache\n");

        if (mem_section_is_ptr)
                get_symbol_data("mem_section", sizeof(void *), &addr);
        else
                addr = symbol_value("mem_section");

        readmem(addr, KVADDR, vt->mem_sec, mem_section_size,
                "memory section root table", FAULT_ON_ERROR);

 * good dump 

crash64>  p &mem_section
p: per_cpu_symbol_search(&mem_section): NULL
GETBUF(344 -> 0)
$1 = (struct mem_section ***) 0xffffff9d4e1a0010 <mem_section>
FREEBUF(0)


 7135 <readmem: ffffffd3bf5eb880, KVADDR, "memory section root table", 32768, (FOE), 556a792d83e0>
 7136 <read_ramdump: addr: ffffffd3bf5eb880 paddr: 17f5eb880 cnt: 1920>
 7137 read_ramdump: addr: ffffffd3bf5eb880 paddr: 17f5eb880 cnt: 1920 offset: 3f5eb880
 7138 <read_ramdump: addr: ffffffd3bf5ec000 paddr: 17f5ec000 cnt: 4096>
 7139 read_ramdump: addr: ffffffd3bf5ec000 paddr: 17f5ec000 cnt: 4096 offset: 3f5ec000
 7140 <read_ramdump: addr: ffffffd3bf5ed000 paddr: 17f5ed000 cnt: 4096>

crash64> rd 0xffffff9d4e1a0010 100
<addr: ffffff9d4e1a0010 count: 100 flag: 490 (KVADDR)>
<readmem: ffffff9d4e1a0010, KVADDR, "64-bit KVADDR", 8, (FOE), 7ffea347dc28>
<read_ramdump: addr: ffffff9d4e1a0010 paddr: a25a0010 cnt: 8>
read_ramdump: addr: ffffff9d4e1a0010 paddr: a25a0010 cnt: 8 offset: 225a0010
ffffff9d4e1a0010:  ffffffd3bf5eb880 <readmem: ffffff9d4e1a0018, KVADDR, "64-bit KVADDR", 8, (FOE), 7ffea347dc28>


./crash64 --minimal SYS_COREDUMP vmlinux

./crash64 --minimal SYS_COREDUMP --kaslr=0x1d43c00000 vmlinux

+ Recent posts