x86 명령어에는 자체 인코딩과 모든 인수가 동시에 메모리에 있어야합니까?


64

RAM이 단일 물리적 페이지로만 백업되는 Linux VM을 실행할 수 있는지 여부를 파악하려고합니다.

이를 시뮬레이션하기 위해 KVM에서 중첩 된 페이지 결함 핸들러를 수정하여 현재 처리 된 페이지 결함에 해당하는 것을 제외하고 모든 중첩 된 페이지 테이블 (NPT) 항목에서 현재 비트를 제거했습니다.

Linux 게스트를 시작하려고 할 때 메모리 피연산자를 사용하는 어셈블리 명령어는 다음과 같습니다.

add [rbp+0x820DDA], ebp

명령어가 포함 된 페이지와 피연산자에서 참조 된 페이지 (이 예제에서 [rbp+0x820DDA]) 의 현재 비트를 복원 할 때까지 페이지 결함 루프가 발생합니다 .

이것이 왜 그런지 궁금합니다. CPU가 순차적으로 메모리 페이지에 액세스해서는 안됩니다. 즉, 먼저 명령어를 읽은 다음 메모리 피연산자에 액세스해야합니까? 아니면 x86에서 모든 피연산자 페이지뿐만 아니라 명령어 페이지도 동시에 액세스 할 수 있어야합니까?

AMD Zen 1에서 테스트 중입니다.


2
왜 이렇게 하시겠습니까?
SS Anne

11
기술적 인 관심사 :)
savvybug

14
재미있는 프로젝트 아이디어를지지합니다.
파이프

10
이것은 "브라우저에서 JavaScript로 실행되는 486 에뮬레이터에서 부팅 리눅스"수준에 미쳤다. 나는 그것을 좋아한다.
chrylis

3
분명히, 나는이 질문을 당신이 이미 생각하고있는 것과 같은 논리적 인 결론에 대해, 앞으로의 진전을 보장하기위한 최소한의 작업 세트에 관한 것입니다. 나는 당신이 그 질문에 새로운 첫 단락을 추가하기 전에 이미 대답했습니다. : PI는 일부 링크와 세부 정보를 몇 군데 추가했습니다 (예 : 페이지 워커는 일부 게스트 페이지 디렉토리 항목을 내부적으로 캐시 할 수 있음).이 질문은 HNQ로 인해 예상보다 훨씬 더 많은 관심을 받고 있기 때문입니다.
Peter Cordes

답변:


56

예, 머신 코드와 모든 메모리 피연산자가 필요합니다.

CPU가 순차적으로 메모리 페이지에 액세스해서는 안됩니다. 즉, 먼저 명령어를 읽은 다음 메모리 피연산자에 액세스해야합니까?

그렇습니다. 논리적으로 일어나는 일이지만 페이지 오류 예외는 해당 2 단계 프로세스를 중단하고 진행 상황을 버립니다. CPU는 페이지 결함이 발생했을 때 어떤 명령이 있었는지 기억할 방법이 없습니다.

유효한 페이지 결함을 처리 한 후 페이지 결함 핸들러가 리턴되면 RIP = 결함 명령의 주소이므로 CPU 는 처음부터 실행을 재 시도합니다 .

OS가 결함 명령의 기계 코드를 수정 iret하고 페이지 결함 핸들러 (또는 기타 예외 또는 인터럽트 핸들러) 이후에 다른 명령을 실행하는 것이 합법적입니다 . 따라서 AFAIK는 CPU가 CS : RIP에서 코드 페치를 다시 실행해야한다는 것을 아키텍처 적으로 요구합니다. (하드 페이지 결함 디스크를 기다리거나 잘못된 페이지 결함의 신호 처리기에 SIGSEGV를 전달하는 동안 다른 프로세스를 예약하는 대신 결함이있는 CS : RIP로 돌아가는 것으로 가정합니다.)

하이퍼 바이저 입력 / 종료에도 아키텍처 상 필요합니다. 종이에 명시 적으로 금지되어 있지 않더라도 CPU 작동 방식이 아닙니다.

@torek 씨는 일부 (CISC) 마이크로 프로세서가 부분적으로 명령어를 해독하고 페이지 결함 에서 마이크로 레지스터 상태를 덤프 하지만 x86은 그렇지 않다고 말합니다.


일부 명령어는 인터럽트 가능하며 (can의 rep movsmemcpy) 및 기타 문자열 명령어와 같은 부분 진행을 수행 하거나로드 / 스 캐터 저장소를 수집 할 수 있습니다. 그러나 유일한 메커니즘은 문자열 연산을위한 RCX / RSI / RDI 또는 수집을위한 대상 및 마스크 레지스터 (예 : AVX2vpgatherdd 수동 ) 와 같은 아키텍처 레지스터를 업데이트하는 것 입니다. opcode / 디코딩을 유지하지 않으면 일부 숨겨진 내부 레지스터가 생성되고 페이지 결함 핸들러에서 iret 후에 다시 시작됩니다. 이들은 여러 개의 개별 데이터 액세스를 수행하는 명령입니다.

또한 x86 (대부분의 ISA와 마찬가지로)은 명령어가 원자 성임을 보증합니다. 인터럽트 / 예외 : 인터럽트 전에 완전히 발생하거나 전혀 발생하지 않습니다. 작동하는 동안 어셈블리 명령을 중단합니다 . 따라서 예를 들어 접두사가 add [mem], reg없는 경우에도 상점 부품에 결함이있는 경우로드를 폐기해야 lock합니다.


앞으로 진행하기 위해 존재하는 최악의 게스트 사용자 공간 페이지 수는 6 개 (각각 별도의 게스트 커널 페이지 테이블 하위 트리)가 될 수 있습니다.

  • movsq또는 movsw페이지 경계에 걸쳐있는 2 바이트 명령어이므로 두 페이지를 모두 해독해야합니다.
  • qword 소스 피연산자 [rsi]도 페이지 분할
  • qword 대상 피연산자 [rdi]도 페이지 분할

이 6 페이지 중 하나라도 오류가 발생하면 다시 정사각형으로 돌아갑니다.

rep movsd또한 2 바이트 명령어이므로 한 단계 씩 진행해도 동일한 요구 사항이 적용됩니다. 잘못 정렬 된 스택과 같 push [mem]거나 이와 유사한 경우가 pop [mem]있을 수 있습니다.

수집로드 / 스 캐터 저장소를 "인터럽트 가능"하게 만드는 이유 (또는 부수적 인 이점 중 하나) (마스크 벡터를 진행 상태로 업데이트)는 단일 명령을 실행하기 위해이 최소 풋 프린트를 늘리지 않기위한 것입니다. 또한 한 번의 수집 또는 분산 중에 여러 결함을 처리하는 효율성을 향상시킵니다.


@Brandon 은 게스트가 메모리에 페이지 테이블이 필요하고 사용자 공간 페이지 분할도 1GiB 분할이 될 수 있다고 언급하면서 두 측면이 최상위 PML4의 다른 하위 트리에 있다고 지적했다. HW 페이지 워크는 모든 게스트 페이지 테이블 페이지를 터치하여 진행해야합니다. 이 병리학 적 상황은 우연히 일어날 가능성이 적습니다.

TLB (및 페이지 워커 내부)는 일부 페이지 테이블 데이터를 캐시 할 수 있으며 OS invlpg에서 새 CR3 최상위 페이지 디렉토리를 설정 하지 않은 경우 페이지 워크를 처음부터 다시 시작할 필요가 없습니다 . 존재하지 않는 페이지에서 현재 페이지로 페이지를 변경할 때 이들 중 어느 것도 필요하지 않습니다. x86 on paper는 그것이 필요하지 않다는 것을 보장합니다 (따라서 현재 존재하지 않는 PTE의 "네거티브 캐싱"은 허용되지 않으며 최소한 소프트웨어에는 보이지 않습니다). 따라서 게스트 물리적 페이지 테이블 페이지 중 일부가 실제로 존재하지 않더라도 CPU가 VMexit하지 않을 수 있습니다.

PMU 성능 카운터 는 명령어가 해당 명령어 의 PEBS 버퍼쓰려면 perf 이벤트가 필요하도록 활성화 및 구성 할 수 있습니다 . 커널이 아닌 사용자 공간 명령 만 계산하도록 카운터 마스크를 구성하면 사용자 공간으로 돌아올 때마다 카운터가 오버플로되고 샘플이 버퍼에 저장되어 페이지 오류가 발생할 수 있습니다.


15
단일 명령어에 대한 최악의 경우는 " push dword [foo"(또는 심지어는 call [foo])와 같이 "페이지 디렉토리 포인터 테이블 경계"(6 페이지, 6 페이지 테이블, 6 페이지 디렉토리, 6 개의 PDPT 및 1 개의 PML4 추가)에 걸쳐 잘못 정렬 된 모든 항목이있을 수 있습니다 . CPU의 "PEBS 버퍼를 사용한 정확한 이벤트 기반 샘플링"기능을 사용하여 push성능 모니터링 데이터가 PEBS 버퍼에 추가되도록합니다. 보수적 인 "주최자가 제공 한 최소 페이지는 손님이 병리학 적 사례를 진전시킬 수 있도록"최소 16 페이지를 원합니다.
Brendan

4
이런 종류의 일은 CISC-y 아키텍처에서 항상 일반적이었습니다. 일부 마이크로 프로세서는 페이지 결함에서 명령어를 부분적으로 디코딩하고 마이크로 레지스터 상태를 덤프하지만, 다른 프로세서는 "loop-y"명령어 (m68k의 DBRA, Vax의 MOVC3 / MOVC5 등)에 대한 주소 피연산자가 레지스터와 비슷한 것을 요구하지 않습니다. REP MOVS 예제로
torek

1
@Brendan : 누군가 VAX 명령에서 최악의 경우를 약 50 페이지로 계산했습니다. 세부 사항을 잊어 버렸지 만 페이지 경계에 명령 자체를 배치하고 페이지 경계에 걸쳐있는 테이블에서 변환 테이블 조회와 같은 것을 사용하고 페이지 경계에서 간접적으로 (rX) [rY]를 사용하고 곧. 가장 털이 많은 명령어는 최대 6 개의 피연산자를 가져 왔으며 (r0-r5에로드) 6 개 모두 이중 간접이 될 수 있다고 생각합니다.
torek

3
OS는 명령어를 변경할 수 있지만 변경할 수도 있습니다 EIP. 따라서 논리적 후속 질문이 있습니다. 지능적인 명령 패치 체계를 가정 할 때 필요한 최소 페이지 수는 얼마입니까? 예를 들어 정렬되지 않은 값을 정렬 된 스크래치 버퍼에 복사하고 명령을 에뮬레이션하고 IRET을 다음 명령으로 복사합니다.
MSalters

1
OS의 iret명령어가 포함 된 페이지 도 메모리에 있어야합니다. 이것은 1 바이트 명령이므로 하나의 추가 페이지입니다. 페이지 결함 핸들러 인터럽트 주소도 메모리에 있어야하지만 위와 동일한 페이지 일 수 있습니다.
Stig Hemmer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.