반응형

간단한 CRC 체크를 위한 함수는 아래와 같습니다.

 

CRC Check할 데이터의 address(pointer) 및 data length를 parameter로 crc 및 checksum 값을 확인할 수 있습니다.

- crc.h

#ifndef CRC_H
#define CRC_H

#include <stdint.h>
#include <stddef.h>

//EDB88320 Table
static const uint32_t crc32_table[256] = {
    0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
    0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
    0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
    0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
    0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
    0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
    0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
    0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
    0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
    0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
    0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
    0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
    0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
    0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
    0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
    0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
    0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
    0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
    0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
    0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
    0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
    0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
    0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
    0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
    0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
    0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
    0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
    0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
    0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
    0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
    0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};

uint32_t crc32(const uint8_t *data, size_t length);

unsigned short crc16_ccitt( const void *buf, int len);
uint8_t calculate_checksum(const uint8_t *data, uint16_t length);

#endif

 

 

 

- crc.c 

#include <crc.h>

uint32_t crc32(const uint8_t *data, size_t length) {
    uint32_t crc = 0xFFFFFFFF;
    for (size_t i = 0; i < length; i++) {
        uint8_t byte = data[i];
        uint32_t table_index = (crc ^ byte) & 0xFF;
        crc = (crc >> 8) ^ crc32_table[table_index];
    }
    return crc ^ 0xFFFFFFFF;
}

unsigned short crc16_ccitt( const void *buf, int len )
{
	unsigned short crc = 0;
	while( len-- ) {
		int i;
		crc ^= *(char *)buf++ << 8;
		for( i = 0; i < 8; ++i ) {
			if( crc & 0x8000 )
				crc = (crc << 1) ^ 0x1021;
			else
				crc = crc << 1;
		}
	}
	return crc;
}

uint8_t calculate_checksum(const uint8_t *data, uint16_t length) {
    uint8_t checksum = 0;
    while (length--) {
        checksum += *data++;
    }
    return checksum;
}
반응형

'Programming Language > C&C++' 카테고리의 다른 글

[C++] STL 및 Container 종류  (0) 2021.08.25
반응형

- XMODEM

XMODEM은 시리얼 통신 매체를 활용하여 CP/M 플로피 디스크에 사용된 기본 블록 크기인 128Byte를 채택해 128byte의 데이터 단위 전송 및 프로토콜 규약으로 당시 모뎀을 통한 파일 전송을 보다 쉽게 만들기 위해 탄생했습니다.

 

XMODEM은 간단하면서도 신뢰성 있는 데이터 전송방식을 제공하여, 이후 YMODEM, ZMODEM 같은 확장 프로토콜이 탄생할 수 있는 기초가 되었습니다.

 

대부분의 파일 전송 프로토콜과 같이 XMODEM은 원래의 데이터를 일련의 패킷으로 쪼개어 수신자에게 보내며 여기에 추가 정보를 담고 있어서 수신자가 어느 패킷을 올바르게 받았는지를 결정할 수 있게 합니다.

 

128Byte 프로토콜에서 1Kbyte 형태로 발전하기도 했으며, Checksum 이외에 CRC16 형태로 확장되기도 하여 각 상황 및 설정에 맞춰 통신을 할 수 있도록 설계되어야 합니다.

 

이번 글에서는 XMODEM 프로토콜의 작동 방식과 패킷 구조, 전송 흐름을 알아보겠습니다.

 

 

 

- XMODEM 프로토콜의 기본 개념

XMODEM은 양방향 통신을 기반으로 하는 오류 검출 방식을 채택하여 데이터를 전송합니다.

특히 종단 간 오류 제어를 위해 체크섬(Checksum) 방식이 사용됩니다.

 

데이터 전송 시 오류가 발생하면 수신 측에서 오류 패킷을 다시 요청하여 재전송하는 방식으로 파일의 신뢰성을 유지합니다.

 

XMODEM은 전송 중 패킷 손실이나 손상된 데이터를 감지할 수 있어 당시 모뎀 환경에서 매우 유용했습니다.

 

 

 

- Data Packet Structure

XMODEM 패킷은 128바이트 크기의 고정된 데이터 블록으로 이루어져 있습니다.

각 패킷은 다음과 같은 형식으로 구성됩니다.

 

  1. SOH (Start of Header): 1바이트, 패킷의 시작을 나타내며 ASCII 코드 0x01로 표시됩니다.
  2. 블록 번호: 1바이트, 현재 패킷의 순서를 표시합니다. 0부터 시작하여 1씩 증가하며, 255를 넘어가면 다시 0으로 돌아갑니다.
  3. 블록 번호의 보수: 1바이트, 블록 번호의 보수를 저장하여 오류를 검증하는 데 사용됩니다.
  4. 데이터: 128바이트의 실제 데이터 내용입니다. 파일의 마지막 패킷에서는 부족한 바이트를 0x1A(EOF 문자)로 채웁니다.
  5. 체크섬: 1바이트, 오류 검출을 위한 값으로, 데이터 필드의 모든 바이트의 합을 1바이트로 나타냅니다.

Checksum 과 CRC16 프로토콜의 차이는 Data Packet 이후 에러 검출을 위하여 Checksum 1 byte 또는 CRC16의 2 byte 에 따른 데이터 끝부분의 차이가 존재합니다.

 

 

- 기본 패킷(Checksum)

SOH Packet
Number
1's Compliment of
Packet Number
Data Packet Checksum
1 Byte 1 Byte 1 Byte 128 Byte 1 Byte

 

- CRC16 패킷

SOH Packet
Number
1's Compliment of
Packet Number
Data Packet Checksum
1 Byte 1 Byte 1 Byte 128 Byte 2 Byte

 

 

 

- Transfer flow

XMODEM 프로토콜은 간단하지만 규칙적인 전송 과정을 따릅니다. 송신 측과 수신 측이 데이터를 주고받는 과정을 단계별로 살펴보겠습니다.

  1. 전송 시작 신호: 수신 측이 준비되면 ASCII 코드 0x15(NAK) 또는 0x43(C)을 송신 측으로 보내 파일 전송을 시작합니다. 이를 통해 송신 측은 수신 측이 준비되었음을 알 수 있습니다.
  2. 데이터 패킷 전송: 송신 측은 각 데이터 블록을 패킷 구조에 맞추어 순차적으로 전송합니다.
  3. ACK/NAK 확인: 수신 측은 패킷을 수신한 후 체크섬을 확인합니다. 오류가 없는 경우 ACK(0x16) 신호를, 오류가 있는 경우 NAK 신호를 송신 측에 보냅니다. NAK(0x15) 신호를 받으면 송신 측은 해당 패킷을 다시 전송합니다.
  4. 파일 전송 완료: 모든 데이터 패킷 전송이 완료되면 송신 측은 EOT(End of Transmission) 신호를 보내 파일 전송을 마무리합니다. 수신 측이 EOT(0x04) 신호를 확인하면 전송이 끝났음을 인지하게 됩니다.

 

아래의 표와 같이 위에 설명한 수신 측에서 CRC의 경우 0x43('C')를 전송하여 전송측에 전송 요청을 보내어 통신 시작 및 싱크를 맞춰 진행합니다.

 

CRC 방식이 아닌 Checksum의 경우 0x15(NAK)를 보내어 시작합니다. 

전송 측으로 전달된 첫 패킷을 수신측에서 제대로 수신할 수 없는 경우 Checksum에서는 0x15(NAK)를 보내지만 CRC의 경우 0x43('C') 값을 보내야 합니다.

 

해당 상황에서 CRC 전송에서 첫 패킷에 0x15(NAK)를 보내게 될 경우 Tera Term 등의 프로토콜에서는 자동으로 Checksum으로 전환되니 해당 시퀀스에 대해 주의해야 합니다.

 

 

전송 시작 이후 SOH 와 각 Data Format에 맞춰 파일 데이터를 수신하며 Checksum 또는 CRC 패킷을 통해 Data 오류 검출 및 정합성을 확인하여 파일 데이터를 수신하고 EOT를 수신하게 되면 통신이 모두 종료되게 됩니다.

 

결론

XMODEM은 초기 파일 전송 프로토콜로서 통신 기술 발전에 중요한 기여를 했습니다.

이 프로토콜은 기본적인 오류 검출 기능을 통해 파일 전송의 신뢰성을 보장하며, 데이터 통신의 기초 개념을 잘 보여줍니다.

 

XMODEM을 이해하면, 이후 발전한 다양한 프로토콜의 배경과 원리를 쉽게 이해할 수 있습니다.

이상으로 XMODEM의 개념과 작동 방식을 알아보았습니다.

반응형
반응형

ARM aarch64 System에서 Ubuntu 22.04 Base Rootfs 작업을 위해서는 하기와 같은 작업을 통해 진행해야 한다.

 

작업을 할 Host PC에는 Qemu 명령이 설치 되어 있어야 하고 없다면 하기 명령을 통해 미리 설치해준다.

 

sudo apt update -y
sudo apt install -y qemu-user-static qemu-aarch64-static

 

 

Ubuntu-Base 22.04 등의 Release 버전은 하기의 링크에서 다운로드가 가능하다.

https://cdimage.ubuntu.com/ubuntu-base/releases/

 

Index of /ubuntu-base/releases

 

cdimage.ubuntu.com

 

위의 해당 사이트를 통해 'ubuntu-base-22.04-base-amd64.tar.gz' 를 다운로드 한 것을 가정으로 작업 방법에 대해 설명한다.

 

다운로드 이후 하기의 명령과 같이 ubuntu_base 디렉토리를 생성 후 압축 파일을 해제하여 작업할 영역을 생성해준다.

chroot 명령을 통해 aarch64 시스템을 사용해야 하기 때문에 'qemu-aarch64-static' 또한 root filesystem의 bin 안에 qemu-aarch64-static을 복사해준다.

mkdir -p ./ubuntu_base
tar zxvf ubuntu-base-22.04-base-amd64.tar.gz -C ubuntu_base
sudo cp /usr/bin/qemu-aarch64-static ./ubuntu_base/bin/qemu-aarch64-static

 

 

ubuntu_base Filesystem이 생성되면 작업 시 사용할 proc 및 sysfs 등을 mount 해주어야 하는데 하기의 스크립트를 통해

chroot 이전에 mount/umount 작업을 진행할 수 있도록 준비한다.

 

#mnt_ubuntu.sh

#!/bin/bash
mnt() {
    echo "MOUNTING"
    sudo mount -t proc /proc ${2}proc
    sudo mount -t sysfs /sys ${2}sys
    sudo mount -o bind /dev ${2}dev
    sudo mount -o bind /dev/pts ${2}dev/pts
    sudo chroot ${2}
}
umnt() {
    echo "UNMOUNTING"
    sudo umount ${2}proc
    sudo umount ${2}sys
    sudo umount ${2}dev/pts
    sudo umount ${2}dev
}

if [ "$1" == "-m" ] && [ -n "$2" ] ;
then
    mnt $1 $2
elif [ "$1" == "-u" ] && [ -n "$2" ];
then
    umnt $1 $2
else
    echo ""
    echo "Either 1'st, 2'nd or both parameters were missing"
    echo ""
    echo "1'st parameter can be one of these: -m(mount) OR -u(umount)"
    echo "2'nd parameter is the full path of rootfs directory(with trailing '/')"
    echo ""
    echo "For example: ch-mount -m /media/sdcard/"
    echo ""
    echo 1st parameter : ${1}
    echo 2nd parameter : ${2}
fi

 

위의 스크립트 실행 방법은 './mnt_ubuntu.sh -m ./ubuntu_base' './mnt_ubuntu.sh -u ./ubuntu_base' 와 같이 작업 시작 전에 '-m' 을 통해 mount 후 작업 완료 시 '-u'를 통해 umount 작업을 진행한다.

 

ubuntu base 작업 이전에 dns 등 network 환경을 잡아주기 위하여 하기와 같이 resolv.conf를 복사해준다.

 

cp -vrfp /etc/resolv.conf ubuntu_base/etc/resolv.conf

 

또는 하기와 같이 nameserver를 8.8.8.8로 설정하여 google nameserver를 활용한다.

 

 

위와 같은 네트워크 환경까지 준비가 되면 하기의 명령을 통해 chroot을 진행하여 filesystem 작업을 진행한다.

 

chmod a+x mnt_ubuntu.sh
./mnt_ubuntu.sh -m ubuntu_base/

 

해당 명령을 진행하면 ubuntu_base 디렉토리를 기준으로 chroot가 진행되어야 한다.

 

 

정상적으로 전환이 완료되었다면 하기와 같은 필수 유틸을 설치하고 필요한 명령 및 패키지가 있다면 apt를 통해 설치를 해준다.

chmod 777 /tmp
apt update -y
apt install -y systemd
apt install -y vim htop
apt install -y net-tools ethtool ifupdown udhcpc ssh iputils-ping rsyslog

 

이와 더불어 추가적으로 ttyS0 등의 getty 로그인이 필요하다면 하기의 내용을 토대로
'./etc/systemd/system/getty.target.wants/getty@ttyS0.service' 경로에 파일을 추가해준다.

 

#  SPDX-License-Identifier: LGPL-2.1-or-later
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Getty on %I
Documentation=man:agetty(8) man:systemd-getty-generator(8)
Documentation=http://0pointer.de/blog/projects/serial-console.html
After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
After=rc-local.service

# If additional gettys are spawned during boot then we should make
# sure that this is synchronized before getty.target, even though
# getty.target didn't actually pull it in.
Before=getty.target
IgnoreOnIsolate=yes

# IgnoreOnIsolate causes issues with sulogin, if someone isolates
# rescue.target or starts rescue.service from multi-user.target or
# graphical.target.
Conflicts=rescue.service
Before=rescue.service

# On systems without virtual consoles, don't start any getty. Note
# that serial gettys are covered by serial-getty@.service, not this
# unit.
ConditionPathExists=/dev/tty0

[Service]
# the VT is cleared by TTYVTDisallocate
# The '-o' option value tells agetty to replace 'login' arguments with an
# option to preserve environment (-p), followed by '--' for safety, and then
# the entered username.
ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear %I $TERM
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
IgnoreSIGPIPE=no
SendSIGHUP=yes

# Unset locale for the console getty since the console has problems
# displaying some internationalized messages.
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION

[Install]
WantedBy=getty.target
DefaultInstance=ttyS0

 

 

위 과정이 완료되면, 하기와 같이 'exit' 명령을 통해 chroot를 종료한다. 그 후 이전에 mount 한 proc 등 sysfs 를 스크립트를 실행하여 종료한다.

./mnt_ubuntu.sh -u ubuntu_base/

 

또한, 사용할 이미지에 이미 넣어두었던 qemu 파일을 aarch64 시스템에서는 필요하지 않으므로 삭제해준다.

rm -rf ./ubuntu_base/bin/qemu-aarch64-static

 

 

해당 작업이 완료되었다면 다시 ubuntu_base 디렉토리로 이동하여 '/' 경로에서 압축을 진행해주어 파일시스템 만드는 것을 완료한다.

 

cd ubuntu_base
tar zcvf ubuntu_base.tar.gz *
mv ubuntu_base.tar.gz ../
cd -
반응형
반응형

 

AArch64(ARMv8) Page Table의 Normal Memory에 대한 속성에 대하여 설명하겠습니다.

 

메모리 영역에 대하여 속성들(ex. memory type, cacheability, shareability...)은 해당 메모리 영역과 관련되어 속성에 의해 제어됩니다.

 

Normal Memory는 일반적으로 페이지로 구성 및 나누어지고 Page Table을 탐색하도록 구현된 프로세서의 하드웨어 메커니즘에 의해 접근됩니다.

 

각각의 메모리 접근은 Page Table Entry와 관련된 Attribute(속성)에 의해 제어되고 제한됩니다.

 

- Table Descriptors

이 글에서는 다양한 페이지 테이블 항목의 형식에 대해 설명합니다. 빠른 참조를 위해 Format 형식을 상단에 배치해두겠습니다.

 

페이지 테이블의 테이블 디스크립터 항목은 비트[1:0]가 0b11이며, AArch64에서 Stage 3까지 지원하기에 Stage 3의 descriptor 형식은 0b11이 될 수 없습니다.

 

- Table descriptor format

AArch64 Page Table Descriptor Type (TG Size 4KB)

 

- Table descriptor attributes

Stage 2 테이블 디스크립터에는 속성 필드가 포함되지 않음을 유의하세요. 다음 표는 Stage 1 테이블 항목에 연결된 속성을 설명합니다.

AArch Page Table descriptor attributes

 

 NSTable

 

이 비트는 디스크립터의 테이블 식별자가 보안 상태(Secure state)에서의 접근을 위해 보안 PA(Secure PA) 공간(NSTable == 0) 또는 Non-Secure(NSTable == 1) 메모리에 위치하는지를 나타냅니다.

 

NSTable이 1이면, 후속 조회되는 하위 페이지 또는 블록 디스크립터의 NS 비트 값은 무시되며 참조된 블록이나 페이지는 비보안 메모리에 속하게 됩니다.

 

또한, 이후 조회된 테이블에서의 NSTable 값도 무시되며 이러한 테이블 항목들은 비보안 메모리를 참조합니다.

 

추가적으로, 보안 상태에서 가져온 항목은 nG == 1(non-global)로 간주됩니다.

 

 

참고: NSTable 비트의 설정은 변환 테이블 탐색의 모든 후속 조회에 적용됩니다.

 

 

 APTable

 

APTable 비트는 변환의 한 단계에서 이후 조회되는 항목들에 대한 권한을 설정합니다. 이는 동일한 변환 단계 내에서 적용됩니다.

 

Secure EL3 및 Non-Secure EL2의 경우  APTable 비트 0이 RES0로 설정되어 비트 1만 유효합니다.

 

APTable[1:0]이 0b1x로 설정되면, 이후 레벨 조회의 권한과 상관없이 모든 예외 레벨에서 쓰기 권한이 허용되지 않습니다.

 

 

여러 단계 변환이 있는 TR(Translation Regimes)에서는 APTable이 Stage 1 변환 제어만 담당합니다. Stage 2 디스크립터의 경우, APTable 비트가 RES0로 설정되어 하드웨어가 해당 값을 무시합니다.

 

 

여러 예외 레벨 변환 체제를 위한 APTable 비트의 전체 설명은 다음과 같습니다.

APTable fields

 

 UXNTable/XNTable

Unprivileged eXecute Never (UXN) / eXecute Never (XN)을 나타냅니다.

 

Stage 1에서 여러 VA(Virtual Address) 범위가 지원되는 경우, 이 필드는 UXNTable이라고 불리며, 이후 레벨 조회에서 참조된 영역에서 가져온 명령어가 EL0에서 실행될 수 있는지 여부를 결정합니다.

 

Stage 1에서 하나의 VA만 지원되는 경우, 이 필드는 XNTable이며 동일한 변환 단계에서 eXecute Never 동작을 제어합니다.

 

  • UXNTable 비트가 1로 설정되면, 실제 디스크립터에 설정된 값과 관계없이 모든 후속 레벨 조회에서 UXN 비트가 설정된 것으로 간주됩니다.
  • XNTable 비트가 1로 설정되면, 실제 디스크립터에 설정된 값과 관계없이 모든 후속 레벨 조회에서 XN 비트가 설정된 것으로 간주됩니다.
  • 0으로 설정되면, 이 비트는 아무런 영향을 미치지 않습니다.

 

 PXNTable

 

Privileged eXecute Never(PXN)을 나타냅니다.

이 비트는 두 개의 VA(Virtual Address) 범위를 지원할 수 있는 Stage 1 변환에서만 유효하며, 하나의 VA 범위만 지원하는 Stage 1 변환에서는 RES0로 간주됩니다.

 

PXNTable 비트가 1로 설정되면, 실제 디스크립터에 설정된 값과 상관없이 이후 모든 레벨 조회에서 PXN 비트가 1로 설정된 것으로 간주됩니다.

 

이에 따라 해당 영역에서 가져온 명령어는 EL1 및 더 높은 예외 레벨에서 실행될 수 없습니다.

 

 

- Block/Page descrioptors

 

이 글에서는 다양한 페이지 테이블 항목의 형식에 대해 설명합니다.

 

페이지 테이블의 블록 디스크립터 항목은 비트[1:0]이 0b01입니다.

 

페이지 디스크립터 항목은 비트[1:0]이 0b11이며, AARCH64에서의 페이지 레벨은 반드시 3개 이어야 합니다.

 

AArch64 Page descriptor format (TG size = 4KB)

 

AArch64 Block descriptor format (TG size = 4KB)

 

 

 

페이지 테이블 항목(블록 디스크립터, 페이지 디스크립터, 테이블 디스크립터)의 필드 의미는 사용 중인 변환 체제(translation regime)에 따라 달라집니다.

 

- Page/Block descriptor attributes

 

AArch64 Page/Block descriptor attributes

 

UXN


비트 54는 Stage 1에서 두 개의 VA(Virtual Address) 범위를 지원하는 변환 체제에 대한 Stage 1 테이블 항목에서만 UXN으로 정의됩니다. 이 필드는 EL0 코드 실행에만 적용됩니다.

  • 0일 경우, EL0에서 코드 실행이 허용됩니다.

XN


비트 54는 Stage 1에서 하나의 VA 범위만 지원하는 변환 체제에 대해 XN으로 정의됩니다. 이 필드는 변환이 적용되는 모든 예외 레벨에 적용됩니다.

  • 0일 경우, 코드 실행이 허용되며, 그렇지 않으면 실행이 금지됩니다.

PXN


비트 53은 Stage 1에서 두 개의 VA 범위를 지원하는 변환 체제에 대한 Stage 1 테이블 항목에서만 PXN으로 정의됩니다.

 

하나의 VA 범위만 지원하는 TR에서는 이 비트가 RES0로 설정되어 하드웨어에서 무시됩니다.

 

이 필드는 EL0 이상의 예외 레벨에서만 적용됩니다.

  • 0일 경우, 코드 실행이 허용됩니다.

 

Contiguous


Stage 2의 초기 조회에만 적용되며, 동일한 속성 및 권한을 공유하는 항목들이 단일 TLB 항목에 캐시될 수 있음을 하드웨어에 정보를 제공합니다.

 

 

DBM (Dirty Bit Modification)


이 비트는 페이지 또는 메모리 섹션의 내용이 수정되었음을 나타냅니다.

 

하드웨어 액세스 플래그 관리가 활성화되면 이 비트는 하드웨어에서 옵션으로 제어될 수 있습니다.

이를 활성화하려면 TCR_ELx 레지스터의 HD 필드(TCR_ELx.HD)를 설정합니다.

 

이 기능은 하드웨어 구현에 따라 옵션으로 제공되고 지원되지 않으면 TCR_ELx.HD는 RES0로 간주됩니다.

 

 

nG (non-Global)


비트 11은 Stage 1에서 두 개의 VA 범위를 지원하는 변환 체제의 테이블 항목에서만 nG로 정의됩니다.

하나의 VA 범위를 지원하는 TR에서는 이 비트가 RES0로 설정되고 하드웨어에서 무시됩니다.

 

AF (Access Flag)

이 비트는 ARMv8.0에서 소프트웨어에 의해 관리되며, ARMv8.1-TTHM 확장이 구현된 경우 하드웨어에 의해 옵션으로 관리될 수 있습니다.

 

이 비트는 페이지나 블록이 처음 액세스되었을 때 설정됩니다.

 

AF == 0으로 설정된 디스크립터 항목이 TLB로 읽히려고 할 때마다 액세스 오류가 발생하며, 소프트웨어는 해당 디스크립터의 AF를 1로 설정해야 합니다.

 

 

SH[1:0] (Shareability)


이는 캐시 가능한 메모리에만 적용되며(AttrInx[2:0]에 의해 결정), Stage 1 및 Stage 2에서 다음과 같은 의미를 가집니다(0b01은 예약됨):

  • 0b00: 비공유 가능
  • 0b10: 외부 공유 가능
  • 0b11: 내부 공유 가능

 

AP[2:1] (Access Permissions)


Stage 1 변환에서(여러 예외 레벨을 포함하는 TR의 경우):

  • 0b00: EL0에서 액세스 불가, 상위 레벨에서 읽기/쓰기 가능
  • 0b01: EL0에서 읽기/쓰기 가능, 상위 레벨에서 읽기/쓰기 가능
  • 0b10: EL0에서 액세스 불가, 상위 레벨에서 읽기만 가능
  • 0b11: EL0에서 읽기만 가능, 상위 레벨에서 읽기만 가능

Stage 1 변환(TR이 단일 예외 레벨을 포함하는 경우), AP[1]은 RES1로 설정됩니다:

  • 0b0: 읽기/쓰기 가능
  • 0b1: 읽기만 가능

Stage 2 변환에서는 S2AP 필드가 액세스 권한에 영향을 미치며 AP[2:1]과 결합됩니다.

 

 

NS (Non-Secure Access Control)

  • 0b1: 보안 및 비보안 실행 상태에서 액세스 허용
  • 0b0: 보안 실행 상태에서만 액세스 허용

 

MemAttr[3:0] (Memory Attribute Index)


MAIR_ELx 레지스터에서 메모리 속성 인덱스를 나타내며, 디바이스 메모리와 일반 메모리를 구분할 수 있습니다.

 

일반 메모리의 경우 캐시 가능성(Writeback/Writethrough), 공유 가능성(내부/외부), 할당 정책(Read/Write) 등의 정보를 포함합니다.

 

Writable 메모리에서 실행 방지


추가 보안 조치로 AArch64는 모든 예외 레벨과 변환 체제에서 쓰기 가능한 메모리에서의 실행을 방지하는 기능을 제공합니다.

 

이 기능은 레지스터 필드 SCTLR_ELx.WXN에 의해 제어됩니다.

EL0와 상위 EL에 적용되는 TR에서 해당 SCTLR_ELx.WXN == 1로 설정되면:

  • Stage 1 변환의 EL0에서 쓰기 가능한 모든 영역은 XN == 1로 처리됩니다.
  • Stage 1 변환의 EL1에서 쓰기 가능한 모든 영역은 PXN == 1로 처리됩니다.

단일 예외 레벨에만 적용되는 TR에서 SCTLR_ELx.WXN == 1로 설정되면, Stage 1 변환에서 쓰기 가능한 모든 영역은 UXN == 1로 처리됩니다.

반응형

'System Programming > Arm' 카테고리의 다른 글

라즈베리파이(RPI)4 OpenOCD JTAG 연결 디버깅 방법  (0) 2023.06.18
Rockchip AP 부팅 흐름 정리  (0) 2020.03.11
ARM Processor 7개의 Mode  (0) 2016.08.29
ARM Processor 개요  (0) 2016.08.29
반응형

임베디드 시스템 개발 및 분석 중 라즈베리 파이4를 통해 디버깅 및 테스트를 하는 것이 제일 간단하고 유용할 것으로 예상되어 찾아본 결과 라즈베리파이4 자체의 JTAG Pin을 활용하여 ARM64 환경의 Bare-Metal Code 테스트를 진행 할 수 있으며 쉽게 테스트 및 디버깅이 가능하여 테스트 및 응용에 매우 강력하고 유용한 플랫폼으로 활용이 가능할 것으로 생각한다.

 

대략적인 연결 방법은 라즈베리파이4 Bare-Metal Code 로드 및 디버깅과 커널 디버깅에 사용할 수 있도록 OpenOCD를 사용하여 JTAG Interface를 활용 및 연결해야 한다.

그러기 위해서 라즈베리파이의 JTAG Pin을 소유하고 있는 JTAG Debugger (Olimex ARM-USB-OCD-H) 연결하여 TAP 인식 및 ARM Core Debugging 이 가능하다.

 

라즈베리파이4의 JTAG Pin 위치는 하기와 같다.

위에 나와있는 그림처럼 RPI의 JTAG Pin을 참고하여 사용하고자 하는 JTAG(여기서는 Olimex ARM-USB-OCD-H)의 Pin Map을 확인하여 1대1 연결하여 JTAG 통신 환경을 구축한다.

 

라즈베리파이에서 JTAG Pin을 사용하기 위해서는 Default Pin Func(Mux)가 JTAG 용도로 정의 되어 있지 않기 때문에

하기와 같이 booting 시 config.txt에 기입하여 Pin Mux를 진행하여야 한다.

 

[all]
# Disable pull downs
gpio=22-27=np

# Enable jtag pins (i.e. GPIO22-GPIO27)
enable_jtag_gpio=1

 

OpenOCD에서 rpi4 디버깅 연결을 위하여 하기와 같이 rpi4 관련된 config 파일을 생성해야 하는데 하기와 같이 작성하여

rpi4.cfg를 /share/openocd/script/targets 내에 저장해준다.

 

# SPDX-License-Identifier: GPL-2.0-or-later

# The Broadcom BCM2711 used in Raspberry Pi 4
# No documentation was found on Broadcom website

# Partial information is available in raspberry pi website:
# https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/

if { [info exists CHIPNAME] } {
    set  _CHIPNAME $CHIPNAME
} else {
    set  _CHIPNAME bcm2711
}

if { [info exists CHIPCORES] } {
    set _cores $CHIPCORES
} else {
    set _cores 4
}

if { [info exists USE_SMP] } {
    set _USE_SMP $USE_SMP
} else {
    set _USE_SMP 0
}

if { [info exists DAP_TAPID] } {
    set _DAP_TAPID $DAP_TAPID
} else {
    set _DAP_TAPID 0x4ba00477
}

jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4
adapter speed 3000

dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu

# MEM-AP for direct access
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0

# these addresses are obtained from the ROM table via 'dap info 0' command
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}

set _smp_command "target smp"

for { set _core 0 } { $_core < $_cores } { incr _core } {
    set _CTINAME $_CHIPNAME.cti$_core
    set _TARGETNAME $_CHIPNAME.cpu$_core

    cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]
    target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME

    set _smp_command "$_smp_command $_TARGETNAME"
}

if {$_USE_SMP} {
    eval $_smp_command
}

# default target is cpu0
targets $_CHIPNAME.cpu0

 

 

OpenOCD 실행을 하기와 같이 JTAG Interface와 RPI4에 관련된 cfg 파일을 -f 매개인자를 통하여 실행한다.

 

.\bin\openocd.exe -f .\share\openocd\scripts\interface\ftdi\olimex-arm-usb-ocd-h.cfg -f .\share\openocd\scripts\target\rpi4.cfg

 

 

위의 명령을 통해 OpenOCD를 터미널 혹은 PowerShell 상에서 실행하여  아래의 그림과 같이 GDB Server 와 Telnet Port가 생성되는 것을 확인 할 수 있다.

 

 

GDB Server가 정상적으로 실행되면 Rpi4의 Kernel 또는 Bare-metal Code를 작성하여 빌드 후 테스트 할 수 있는 환경을 갖추게 됩니다.

 

이 이후의 GDB Server Remote 연결 및 GDB 디버깅은 다음 포스트에서 진행하도록 하겠습니다.

 

 

반응형

'System Programming > Arm' 카테고리의 다른 글

ARM64(AArch64) Page Table Normal Memory Attribute  (0) 2024.03.31
Rockchip AP 부팅 흐름 정리  (0) 2020.03.11
ARM Processor 7개의 Mode  (0) 2016.08.29
ARM Processor 개요  (0) 2016.08.29
반응형

Virtualbox 사용 시 윈도우와 서로 파일을 주고 받기 편하기 위해 공유 폴더를 설정 할 수 있습니다.

 

하지만, 아래의 그림처럼 설정을 완료하고 사용하려 해도 권한 문제가 발생한다.

 

 

폴더 권한 예시

 

자동 마운트 되는 경로는 '/media' 내에 모두 디렉토리가 생성되어 마운트 되어 있을 것이다.

아래의 그림과 같이 'media' 경로 안에서 'ls -al' 명령어를 통해 확인해 보면 그룹 권한이 'vboxsf' 로 되어 있다.

디렉토리 권한 체크

위의 상황에서 간단한 해결 방법은 내가 사용하는 유저를 'vboxsf' 그룹에 포함 시켜주어야 한다.

 

vboxsf 그룹 등록 명령

위의 그림과 같이 'sudo gpasswd -a USER_ID vboxsf' 명령을 통해 그룹 등록을 진행해주어야 한다.

주의해야 할 점은 그룹 등록이 완료되었다 하더라도 이미 로그인된 세션에는 해당 권한이 적용되지 않으므로

재부팅 또는 로그아웃을 진행해주어야 한다.

 

재부팅 후 공유 폴더 내용 확인 예시

반응형

+ Recent posts