왜 바이오스 모딩을 해야 했는가
ESXI를 사용하고 싶습니다.
하지만 ESXI는 일반 매인보드들에 대한 하드웨어 모니터링 기능을 재공하지 않습니다.
이문제에 대해서 이전에 애즈락 보드로 SMBUS를 통해 라즈베리 파이로 하드웨어 모니터링 정보를 넘겨주어서 그걸로 인터넷으로 재공하거나 임베디드용 OLED 패널로 디스플레이 하는 일을 해 보았기 때문에 그것 처럼 하면 될 것이라고 생각했습니다..
(해당시점의 후기:http://2cpu.co.kr/bbs/new_view.php?bo_table=freeboard_2011&wr_id=1089440&qstr=&page=2 )
문재점
애즈락 보드의 하드웨어 모니터링을 담당하는 칩은 SMBUS 슬레이브 기능을 재공했기 때문에
UEFI가 해당칩 (super io 누보톤제 NCT6776F,D)을 초기화 한다음 DXE에서 로드되어서 다시 초기화 해 주는 드라이버를 만들어서 올리는 것으로 해결 했습니다..
순정 DXE드라이버는 하드웨어 smbus slave포트를 프린터 포트로 할당하도록 되어 있었기 때문에.
그와 관련된 레지스터를 바꿔 주는 역할을 합니다.
프로토콜 노티파이어 같은걸 꼽는게 귀찮아서 그냥 딜레이 시키는 조잡한 코드지만.. 원하는 대로 잘 작동해 주었습니다.
#include <Uefi.h>
#include <Library/PcdLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Protocol/SimplePointer.h>
#include <Protocol/SuperIo.h>
#include <Protocol/DevicePath.h>
#include <Library/DevicePathLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/BaseMemoryLib.h>
#include <Library/TimerLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/ReportStatusCodeLib.h>
#include <Library/IoLib.h>
#include <Guid/SmBios.h>
#include <Guid/Acpi.h>
#include <Guid/DxeServices.h>
#define SIO_REG_LDSEL 0x07 /* Logical device select */
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
#define SIO_REG_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
#define SIO_NCT6776_ID 0xc330
#define SIO_ID_MASK 0xFFF8
EFI_STATUS
EFIAPI
UefiMain(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE* SystemTable
)
{
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE* HandleBuffer;
EFI_SIO_PROTOCOL* Sio;
UINTN Index;
UINT8 RegValue;
UINT8* pRegValue;
pRegValue = &RegValue;
UINT8 RegWValue;
UINT8* pRegWValue;
pRegWValue = &RegWValue;
MicroSecondDelay(1000000); //노티파이어를 설정해서 프로토콜이 디스패치 되는 순간에 처리하도록
//만들엇어야 하지만 이걸 만들던 당시에는 그런 지식이 없었습니다..
Status = gBS->LocateHandleBuffer(
Protocol,
&gEfiSioProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
//sio 프로토콜은 sio가 초기화 된 다음 디스패치 되므로 sio가 준비된 시점에 작업을 시도하기 위해서 사용했습니다. 사실 설정을 바꾸는것 자체는 ioport로 바로 접근하는것과 별반 다를게 없었습니다.
if (EFI_ERROR(Status)) {
return EFI_NOT_FOUND;
}
for (Index = 0; Index < HandleCount; Index++) {
Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiSioProtocolGuid, (VOID * *)& Sio);
if (EFI_ERROR(Status)) {
continue;
}
Status = Sio->RegisterAccess(Sio, 0, 1, 0xF0, pRegValue);
Status = Sio->RegisterAccess(Sio, 0, 1, 0x07, pRegValue);
if (RegValue == 0xF) {
Status = Sio->RegisterAccess(Sio, 0, 0, 0x2F, pRegValue);
RegWValue = 0x1;
Status = Sio->RegisterAccess(Sio, 1, 0, 0x2F, pRegWValue);
RegWValue = 0x90;
Status = Sio->RegisterAccess(Sio, 1, 0, 0xF0, pRegWValue);
Status = Sio->RegisterAccess(Sio, 0, 0, 0x1A, pRegValue);
RegWValue = 0xF4;
Status = Sio->RegisterAccess(Sio, 1, 0, 0x1A, pRegWValue);
Status = Sio->RegisterAccess(Sio, 0, 0, 0x1B, pRegValue);
RegWValue = 0x42;
Status = Sio->RegisterAccess(Sio, 1, 1, 0x1B, pRegWValue);
}
}
return EFI_SUCCESS;
}
어드반텍 보드도 이와 같은 방식으로 간단히 해결 할 수 있으리라 생각했습니다만.. 아니였습니다.
해당 보드에 들어 있는 8518e 는 애즈락의 노보톤 칩과는 다르게 intel 8032 코어를 탑재한 본격적인 임베디드 컨트롤러로 8비트 컴퓨터였습니다.
처음에는 이 친구의 펌웨어를 어떻게 해 볼까 하고 생각했지만 이건 만만치가 않았습니다.
데이터 시트를 보고 디버그 포트로 내부 플레시에 어찌어찌 접근해서 따 넨다고 쳐도
8032 는 써본적도 들어본적도 없는 프로세서고
설령 어찌저찌 내용을 분석하고 개발도구를 찾아서 새 펌웨어를 만 준비를 할 수 있다고 쳐도
EC의 기능 자체가 컴퓨터의 전원관리와 팬컨트롤등 빼버릴 수 없는 요소들이라
내가 원하는 출력 기능만 딸랑 만들어서 올리면 부팅조차 불가능 하게 될 것이므로
기존의 펌웨어에 있는 핵심기능을 다 구현하지 않으면 안됩니다. 현실적으로 불가능 한 일이므로 일찍이 포기 했습니다.
두번째로 해법이 되지 않을까 하고 생각한 부분은 ME 였습니다.
Q87의 매니지먼트 엔진은 5MB 짜리 펌웨어를 가지고 있는 임베디드 컴퓨터로서 원격재어나
시리얼 콘솔을 이더넷으로 리디렉트 하는 기능도 있는 물건입니다.
막연하게 이더넷으로 중계하는 정보중에 시스템 온도라던가 있을 줄 알았는데..
예 신기하게도 없었습니다 예 하드웨어 모니터링 기능이 전혀 없었습니다 왜일까요?
정말 신기하게도 전혀 없었습니다.
세번째로 타겟이 된것은 UEFI의 SMM 이였습니다. 이 친구는 x86 프로세서에 내장된 기능으로
제가 하려고 하는 용도 그러니까 OS 독립적으로 하드웨어 모니터링을 지원할 수 있는 기능
에 대해서 딱 알맞은 기능입니다.
SMM 기능의 요지는 CPU가 인터럽트를 통해 특수한 작동 모드인 시스탬 매니지먼트 모드로 진입하면
OS의 간섭이나 관리 모니터링에서 벗어난 상태로 UEFI가 smram에 적재한 프로그램을 실행하고 작업이 완료되면 smm모두가 해재되면서 다시 운영체제의 관리하로 돌아가게 되는것입니다.
이것의 장점은 pc의 다른 구성요소들에 액세스 하는것과 달리 빠방한 x86 CPU 본체의 성능을 사용할 수 있으며 CPU가 접근할 수 있는 매모리에 고스란히 접근할 수 있습니다..
애즈락 보드에서 외부 모니터링 시스탬을 만들때도 고려했던 옵션중에 하나지만. 그때는 사용하지 않았었는데 단점이 있습니다.
첫로 CPU의 자원을 인터럽트로 끌어당긴다는건 그만큼 운영체제 하에서 돌아가는 작업이 성능을 빼았긴다는 것입니다. 이건 어짜피 장난감 시스템이라 큰 문제가 안되었습니다.
둘째가 큰데 프로그래밍이 힘듭니다. 절대적으로 힘든건 아니고 저한태는 힘들것 같았습니다.
Smm 기능은 UEFI shell에서 로드가능한 어플리캐이션 형태로 태스트가 가능한 dxe 레벨의 다른 드라이버들과 달리 무조건 UEFI에 넣어서 보드에 올려야 태스트가 가능합니다.
디버깅용 jtag 같은것도 없는 저로서는 한줄 써서 컴파일 해서 플레시에 올려서 부팅되는지 확인하고 한줄 써서 컴파일 해서 플래시에 올려서 부팅되는지 확인하는 짓을 반복해야 할 터였으므로 상당히 답이 없는 작업이 될것 같았습니다. 그래서 당시에는 이 방법을 택하지 않았습니다.
그러나 AIMB-274 에서는 다른 옵션이 없었으므로 smm으로 도전하게 되었습니다.
어눌한 코딩 실력탓에 이런걸 시작할때는 우선 깃헙에서 배낄 물건을 검색하는데..
이번에는https://github.com/Cr4sh/SmmBackdoor
이것이 타겟이 되었습니다. 러시아 아저씨가 만든 물건인데
이 프로그램의 초점은 smram의 내용이 운영체제 하에서는 보이지 않으므로 악성코드나 취약점이 은폐된체로 존재할 수 있기에 smram을 덤프하고 분석할 수 있도록 만든 프로그램이라고 합니다.
저는 딱히 기존의 smram 내용물이나 굴러가는 상황을 확인하고 분석할 필요는 없었으나.
이 프로그램 말고는 딱히 smm드라이버의 레퍼런스가 없었기에 이것을 참고했습니다.
이 드라이버는 DXE + SMM 조합 드라이버로
같은 코드가 두번에 걸쳐서 로드 됩니다.
DXE 레벨에서 한번 SMM 레벨에서 한번 서로서로 접근할 수 있는 영역이 다르므로
DXE로 먼저 로드되어서 DXE레벨에서 해야할 일을 한다음 SMM 드라이버로서 SMRAM에 로드 되도록 하는것이죠..
이 드라이버의 경우는
DXE로 로드되었을때 UEFI 런타임 영역에 smram 덤프를 위한 매모리를 할당하고 디버깅용 출력 시스템 등을 초기화 하고
SMRAM으로 로드된 다음엔 주기적으로 smram의 동태를 smram 외부에 있는 DXE모드에서 할당했던 공간으로 빼돌리는 기능을 합니다…
제 경우는 SMRAM의 동태같은건 별로 궁금하지 않으나 '주기적' 으로 작동한다는 부분이 중요했습니다.
Cr4sh의 SmmBackdoor에서는
EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL
이 프로토콜을 통해서 주기적으로 smram을 모니터링 하는 기능을 구형 있습니다.
나머지 부분을 재거하고 이 부분만 따 왔습니다.
이 과정에서 확인한 부분이 몇가지 있는데
하스웰 부터는 SMM_Code_Chk_En 이라는 기능이 추가 되었다고 합니다.
첫번로 Uefi 런타임 서비스 테이블에 접근하거나 하면 컴퓨터가 멈추게 되었기 문에
그 부분을 제거 했습니다. 런타임 테이블로 접근하는 부분은 모니터링을 위해서 smram 외부에 매모리를 할당하는 기능을 위한 것이였기 때문에. ┛사용하려는 용도에서는 제거해도 별 문제가 없었습니다.
두번로는
제가 가진 보드는 EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL 프로토콜을 통해서 타이머를 등록하면
부팅이 실패한다는 것이였습니다.
제가 가진 보드의 펌웨어를 UEFITOOL로 검색한 결과 EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL 을
디스패치하고 는 있으나. 사용하는 드라이버는 하나도 없었습니다.
그러한 기능이 필요한듯해 보이는 DXE 드라이버는 대신 edk1 부터 사용되던. EFI_SMM_PERIODIC_TIMER_DISPATCH_PROTOCOL 를 사용하고 있었습니다.
AMI사는 오픈소스 개발 도구인 EDK2를 기반으로 해서 펌웨어를 만들고 있습니다.
AMI는 메인보드 재조사는 소스 상태의 펌웨어를 받아서 일부를 수정해서 완성시키는듯 합니다.
아마 매인보드 재조사 쪽에서 사용을 안 할 프로토콜이라 정상적으로 작동하는지 검토하지 않았던것 같습니다.
EFI_SMM_PERIODIC_TIMER_DISPATCH_PROTOCOL 로 교체하자 정상적으로 작동을 하기 시작했습니다..
여기까지 오자 일사 천리였습니다.
#include <FrameworkSmm.h>
#include <Protocol/SmmBase.h>
#include <Protocol/SmmBase2.h>
#include <Protocol/DevicePath.h>
#include <Protocol/SmmCpu.h>
#include <Protocol/SmmIoTrapDispatch2.h>
#include <Protocol/SmmPciRootBridgeIo.h>
#include <Protocol/SmmPeriodicTimerDispatch.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiRuntimeLib.h>
#include <IndustryStandard/PeImage.h>
#include "HmonSmm.h"
#pragma warning(disable: 4244)
#pragma warning(disable: 4101)
#pragma warning(disable: 4189)
typedef void* PVOID;
#define SMBUS_IO_ADDRESS 0xF040
#define TO_MILLISECONDS(_seconds_) ((_seconds_) * 1000)
#define TO_MICROSECONDS(_seconds_) (TO_MILLISECONDS(_seconds_) * 1000)
#define TO_NANOSECONDS(_seconds_) (TO_MICROSECONDS(_seconds_) * 1000)
//--------------------------------------------------------------smbus
#define SMBUS_HSTS 0
#define SMBUS_BERR (BIT3) // BUS Error
#define SMBUS_DERR (BIT2) // Device Error
#define SMBUS_BYTE_DONE_STS (BIT7) // Completion Status
#define SMBUS_BYTE_FAILD (BIT4) // faild
#define SMBUS_BYTE_INTR (BIT1)
//#define SMBUS_BYTE_DONE_STS
#define SMBUS_HCTL 2
#define SMBUS_START (BIT6) // Start/Stop
#define SMBUS_HCTL_CMD_QUICK 0<<2
#define SMBUS_HCTL_CMD_BYTE 1<<2
#define SMBUS_HCTL_CMD_BYTE_DATA 2<<2
#define SMBUS_HCTL_CMD_WORD_DATA 3<<2
#define SMBUS_HCTL_CMD_PROCESS_CALL 4<<2
#define SMBUS_HCTL_CMD_BLOCK 5<<2
#define SMBUS_HCTL_CMD_i2c 6<<2
#define SMBUS_HCMD 3
#define SMBUS_TSA 4
#define SMBUS_RW_SEL_READ 1
#define SMBUS_RW_SEL_WRITE 0
#define SMBUS_HD0 5
#define SMBUS_HD1 6
#define SMBUS_HSTS_ALL 0b11111111
#define SMBUS_LIB_SLAVE_ADDRESS(SmBusAddress) (((SmBusAddress) >> 1) & 0x7f)
#define SMBUS_LIB_COMMAND(SmBusAddress) (((SmBusAddress) >> 8) & 0xff)
#define SMBUS_LIB_LENGTH(SmBusAddress) (((SmBusAddress) >> 16) & 0x3f)
#define SMBUS_LIB_PEC(SmBusAddress) ((BOOLEAN) (((SmBusAddress) & SMBUS_LIB_PEC_BIT) != 0))
#define SMBUS_LIB_RESEARVED(SmBusAddress) ((SmBusAddress) & ~(((1 << 22) - 2) | SMBUS_LIB_PEC_BIT))
#define SMBUS_LIB_RESERVED(SmBusAddress) ((SmBusAddress) & ~(BIT23 - 2))
//--------------------------------------------------------------------------------smbus 상수
#define EC_REG_BASE 0x029C
#define EC_REG_DEVID 0x20 /* Device ID (2 bytes) */
#define EC_IT8518_ID 0x8518
#define EC_IT8528_ID 0x8528
#define IT8518_CMD_PORT 0x029E
#define IT8518_DAT_PORT 0x029F
#define EC_MAX_GPIO_NUM 8UL
#define EC_MAX_ADC_NUM 5UL
#define EC_MAX_FAN_NUM 3UL
#define EC_MAX_BLC_NUM 2UL
#define EC_MAX_SMB_NUM 4UL
#define EC_MAX_WDT_NUM 2UL
#define EC_MAX_DID 32UL
#define SCALE_IN 2933
#define EC_DELAY_MIN 200UL
#define EC_DELAY_MAX 250UL
#define EC_MAX_RETRIES 400UL
//* iManager commands
#define EC_CMD_CHK_RDY 0UL
#define EC_CMD_HWP_RD 0x11UL
#define EC_CMD_HWP_WR 0x12UL
#define EC_CMD_GPIO_DIR_RD 0x30UL
#define EC_CMD_GPIO_DIR_WR 0x31UL
#define EC_CMD_PWM_FREQ_RD 0x36UL
#define EC_CMD_PWM_FREQ_WR 0x32UL
#define EC_CMD_PWM_POL_RD 0x37UL
#define EC_CMD_PWM_POL_WR 0x33UL
#define EC_CMD_SMB_FREQ_RD 0x34UL
#define EC_CMD_SMB_FREQ_WR 0x35UL
#define EC_CMD_FAN_CTL_RD 0x40UL
#define EC_CMD_FAN_CTL_WR 0x41UL
#define EC_CMD_THZ_RD 0x42UL
#define EC_CMD_DEVTBL_RD 0x20UL
#define EC_CMD_FW_INFO_RD 0xF0UL
#define EC_CMD_BUF_CLR 0xC0UL
#define EC_CMD_BUF_RD 0xC1UL
#define EC_CMD_BUF_WR 0xC2UL
#define EC_CMD_RAM_RD 0x1EUL
#define EC_CMD_RAM_WR 0x1FUL
#define EC_CMD_I2C_RW 0x0EUL
#define EC_CMD_I2C_WR 0x0FUL
#define EC_CMD_WDT_CTRL 0x28UL
/* iManager message offsets */
#define EC_MSG_OFFSET(N) (0UL + (N))
#define EC_MSG_OFFSET_CMD EC_MSG_OFFSET(0)
#define EC_MSG_OFFSET_STATUS EC_MSG_OFFSET(1)
#define EC_MSG_OFFSET_PARAM EC_MSG_OFFSET(2)
#define EC_MSG_OFFSET_DATA EC_MSG_OFFSET(3)
#define EC_MSG_OFFSET_MEM_DATA EC_MSG_OFFSET(4)
#define EC_MSG_OFFSET_PAYLOAD EC_MSG_OFFSET(7)
#define EC_MSG_OFFSET_LEN EC_MSG_OFFSET(0x2F)
/* iManager EC flags */
#define EC_IO28_OUTBUF (BIT0)
#define EC_IO28_INBUF (BIT1)
#define EC_F_SUCCESS (BIT0)
#define EC_F_CMD_COMPLETE (BIT7)
#define EC_F_HWMON_MSG (BIT9)
//ACPI RAM Address Table
/* n = 1 ~ 2 */
#define EC_ACPIRAM_ADDR_TEMPERATURE_BASE(n) (0x60 + 3 * ((n)-1))
#define EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(n) EC_ACPIRAM_ADDR_TEMPERATURE_BASE(n)
#define EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(n) (EC_ACPIRAM_ADDR_TEMPERATURE_BASE(n) + 1)
#define EC_ACPIRAM_ADDR_WARNING_TEMPERATURE(n) (EC_ACPIRAM_ADDR_TEMPERATURE_BASE(n) + 2)
/* N = 0 ~ 2 */
#define EC_ACPIRAM_ADDR_FAN_SPEED_BASE(N) (0x70 + 2 * (N))
#define EC_ACPIRAM_ADDR_KERNEL_MAJOR_VERSION 0xF8
#define EC_ACPIRAM_ADDR_CHIP_VENDOR_CODE 0xFA
#define EC_ACPIRAM_ADDR_PROJECT_NAME_CODE 0xFC
#define EC_ACPIRAM_ADDR_FIRMWARE_MAJOR_VERSION 0xFE
#define EC_OFFSET_FAN_ALERT 0x6FUL
#define EC_OFFSET_FAN_ALERT_LIMIT 0x76UL
#define EC_OFFSET_BRIGHTNESS1 0x50UL
#define EC_OFFSET_BRIGHTNESS2 0x52UL
#define EC_OFFSET_BACKLIGHT_CTRL 0x99UL
#define EC_OFFSET_FW_RELEASE 0xF8UL
#define EC_OFFSET_I2C_STATUS 0UL
#define EC_RAM_ACPI 1
//--------------------------------------------------------------------------------ec 상수
EFI_SMM_PERIODIC_TIMER_DISPATCH_CONTEXT m_PeriodicTimerDispatch2RegCtx = { 100000, 640000, 10};
UINT64 m_PeriodicTimerCounter = 0;
EFI_HANDLE m_PeriodicTimerDispatchHandle = NULL;
EFI_SMM_PERIODIC_TIMER_DISPATCH_PROTOCOL* m_PeriodicTimerDispatch = NULL;
EFI_STATUS PeriodicTimerDispatchRegister(EFI_HANDLE* DispatchHandle);
EFI_STATUS PeriodicTimerDispatchUnregister(EFI_HANDLE DispatchHandle);
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* m_TextOutput = NULL;
char* m_PendingOutput = NULL;
EFI_SMM_PCI_ROOT_BRIDGE_IO_PROTOCOL* mSmmPciRootBridgeIo = NULL;
UINT8 cpuTemp, systemTemp;
UINT16 vin12v, vin5v, vin5vsb, vin33v, vinbat;
UINT16 fanRPM1, fanRPM2, fanRPM3;
//---------------------------------------------------------------------timer
PVOID g_BackdoorInfo = NULL;
EFI_SYSTEM_TABLE* gST;
EFI_BOOT_SERVICES* gBS;
EFI_SMM_SYSTEM_TABLE2* gSmst = NULL;
typedef
EFI_STATUS
(EFIAPI* EFI_SMM_HANDLER_ENTRY_POINT2)(
IN EFI_HANDLE DispatchHandle,
IN CONST VOID* Context OPTIONAL,
IN OUT VOID* CommBuffer OPTIONAL,
IN OUT UINTN* CommBufferSize OPTIONAL
);
//-----------------------------------------------------------------------smbus
UINTN
EFIAPI
SmAddrMaker(IN UINT8 regAddr, IN UINT8 slaveAddr)
{
UINTN SmBusAddress;
SmBusAddress = regAddr;
SmBusAddress = SmBusAddress << 7;
SmBusAddress = SmBusAddress + slaveAddr;
SmBusAddress = SmBusAddress << 1;
return SmBusAddress;
}
RETURN_STATUS InternalSmBusAcquire(VOID)
{
__outbyte(SMBUS_IO_ADDRESS + SMBUS_HCTL, 0);
__outbyte(SMBUS_IO_ADDRESS + SMBUS_HD0, 0);
__outbyte(SMBUS_IO_ADDRESS + SMBUS_HD1, 0);
__outbyte(SMBUS_IO_ADDRESS + SMBUS_HSTS, SMBUS_HSTS_ALL);
return RETURN_SUCCESS;
}
RETURN_STATUS InternalSmBusStart(IN UINTN IommBaseAddress, IN UINT8 HostControl)
{
UINT8 HostStatus;
UINT8 COUNT = 0;
//
// Set Host Control Register (Initiate Operation, Interrupt disabled).
//
__outbyte(SMBUS_IO_ADDRESS + SMBUS_HCTL, HostControl + SMBUS_START);
do {
COUNT++;
//
// Poll INTR bit of Host Status Register.
//tegx
HostStatus = __inbyte(SMBUS_IO_ADDRESS + SMBUS_HSTS);
if (COUNT > 20000) {
return RETURN_DEVICE_ERROR;
}
} while ((HostStatus & (SMBUS_BYTE_DONE_STS | SMBUS_DERR | SMBUS_BERR | SMBUS_BYTE_FAILD | SMBUS_BYTE_INTR)) == 0);
if ((HostStatus & (SMBUS_DERR | SMBUS_BERR | SMBUS_BYTE_FAILD)) == 0) {
return RETURN_SUCCESS;
}
//
// Clear error bits of Host Status Register.
//
__outbyte(SMBUS_IO_ADDRESS + SMBUS_HSTS, (SMBUS_DERR | SMBUS_BERR | SMBUS_BYTE_FAILD));
return RETURN_DEVICE_ERROR;
}
UINT16 InternalSmBusNonBlock(IN UINT8 HostControl, IN UINTN SmBusAddress, IN UINT16 Value, OUT RETURN_STATUS* Status)
{
RETURN_STATUS ReturnStatus;
//
// Try to acquire the ownership of QNC SMBUS.
//
ReturnStatus = InternalSmBusAcquire();
if (RETURN_ERROR(ReturnStatus)) {
goto Done;
}
//
// Set Host Commond Register.
//SMBUS_HCMD
__outbyte(SMBUS_IO_ADDRESS + SMBUS_HCMD, (UINT8)SMBUS_LIB_COMMAND(SmBusAddress));
//
// Write value to Host Data 0 and Host Data 1 Registers.
//
__outbyte(SMBUS_IO_ADDRESS + SMBUS_HD0, (UINT8)Value);
__outbyte(SMBUS_IO_ADDRESS + SMBUS_HD1, (UINT8)(Value >> 8));
// Set SMBUS slave address for the device to send/receive from.
//
__outbyte(SMBUS_IO_ADDRESS + SMBUS_TSA, (UINT8)SmBusAddress);
//
// Start the SMBUS transaction and wait for the end.
//
ReturnStatus = InternalSmBusStart(SMBUS_IO_ADDRESS, HostControl);
//
// Read value from Host Data 0 and Host Data 1 Registers.
//
Value = (UINT16)(__inbyte(SMBUS_IO_ADDRESS + SMBUS_HD1) << 8);
Value = (UINT16)(Value | __inbyte(SMBUS_IO_ADDRESS + SMBUS_HD0));
//
// Clear Host Status Register and Auxiliary Status Register.
//
__outbyte(SMBUS_IO_ADDRESS + SMBUS_HSTS, SMBUS_HSTS_ALL);
Done:
if (Status != NULL) {
*Status = ReturnStatus;
}
return Value;
}
UINT8
EFIAPI
SmBusReadDataByte(IN UINTN SmBusAddress, OUT RETURN_STATUS* Status OPTIONAL)
{
return (UINT8)InternalSmBusNonBlock(SMBUS_HCTL_CMD_BYTE_DATA, SmBusAddress | SMBUS_RW_SEL_READ, 0, Status);
}
UINT8
EFIAPI
SmBusWriteDataByte(IN UINTN SmBusAddress, IN UINT8 Value, OUT RETURN_STATUS* Status OPTIONAL)
{
return (UINT8)InternalSmBusNonBlock(SMBUS_HCTL_CMD_BYTE_DATA, SmBusAddress | SMBUS_RW_SEL_WRITE, Value, Status);
}
//-----------------------------------------------------------------------smbus
UINT8 ec_readReg8(UINTN addr, UINT8 reg) {
__outbyte(addr, reg);
return __inbyte(addr + 1);
}
VOID ec_writeReg8(UINTN addr, UINT8 reg, UINT8 val)
{
__outbyte(addr, reg);
__outbyte(addr + 1, val);
}
UINT8 ec_io18_read(UINT8 cmd)
{
return ec_readReg8(IT8518_CMD_PORT, cmd);
}
UINT8 ec_io18_write(UINT8 cmd, UINT8 value)
{
ec_writeReg8(IT8518_CMD_PORT, cmd, value);
return 0;
}
INT32 imanager_check_ec_ready()
{
int retries = 4000000;
do {
if (!ec_io18_read(EC_CMD_CHK_RDY))
return 0;
} while (--retries);
return -1;
}
INT8 ec_read_ACPItemp(UINT8 num)
{
INT8 rlen = 1;
INT32 ret = 0;
ec_io18_write(EC_MSG_OFFSET_LEN, rlen);
ret = imanager_check_ec_ready();
if (ret)
return -1;
ec_io18_write(EC_MSG_OFFSET_PARAM, EC_RAM_ACPI);
ec_io18_write(EC_MSG_OFFSET_DATA, EC_ACPIRAM_ADDR_LOCAL_TEMPERATURE(num));
ec_io18_write(EC_MSG_OFFSET_CMD, EC_CMD_RAM_RD);
ret = imanager_check_ec_ready();
if (ret)
return -1;
ret = ec_io18_read(EC_MSG_OFFSET_STATUS);
if (ret != EC_F_SUCCESS)
return -1;
ret = imanager_check_ec_ready();
if (ret)
return -1;
UINT8 Temptemp = 0;
Temptemp = Temptemp + ec_io18_read(EC_MSG_OFFSET_MEM_DATA);
return Temptemp;
}
INT8 ec_read_ACPItemp_remote(UINT8 num)
{
INT8 rlen = 1;
INT32 ret = 0;
ec_io18_write(EC_MSG_OFFSET_LEN, rlen);
ret = imanager_check_ec_ready();
if (ret)
return -1;
ec_io18_write(EC_MSG_OFFSET_PARAM, EC_RAM_ACPI);
ec_io18_write(EC_MSG_OFFSET_DATA, EC_ACPIRAM_ADDR_REMOTE_TEMPERATURE(num));
ec_io18_write(EC_MSG_OFFSET_CMD, EC_CMD_RAM_RD);
ret = imanager_check_ec_ready();
if (ret)
return -1;
ret = ec_io18_read(EC_MSG_OFFSET_STATUS);
if (ret != EC_F_SUCCESS)
return -1;
ret = imanager_check_ec_ready();
if (ret)
return -1;
UINT8 Temptemp = 0;
Temptemp = Temptemp + ec_io18_read(EC_MSG_OFFSET_MEM_DATA);
return Temptemp;
}
INT16 ec_read_ACPIfanspeed(UINT8 num)
{
INT8 rlen = 2;
INT32 ret = 0;
ec_io18_write(EC_MSG_OFFSET_LEN, rlen);
ret = imanager_check_ec_ready();
if (ret)
return -1;
ec_io18_write(EC_MSG_OFFSET_PARAM, EC_RAM_ACPI);
ec_io18_write(EC_MSG_OFFSET_DATA, EC_ACPIRAM_ADDR_FAN_SPEED_BASE(num));
ec_io18_write(EC_MSG_OFFSET_CMD, EC_CMD_RAM_RD);
ret = imanager_check_ec_ready();
if (ret)
return -1;
ret = ec_io18_read(EC_MSG_OFFSET_STATUS);
if (ret != EC_F_SUCCESS)
return -1;
ret = imanager_check_ec_ready();
if (ret)
return -1;
UINT16 fanspeed = 0;
fanspeed = ec_io18_read(EC_MSG_OFFSET_MEM_DATA);
fanspeed = fanspeed << 8;
fanspeed = fanspeed + ec_io18_read(EC_MSG_OFFSET_MEM_DATA + 1);
return fanspeed;
}
INT16 ec_read_ADC(UINT8 did, UINT8 scaler)
{
INT32 ret = 0;
ret = imanager_check_ec_ready();
if (ret)
return -1;
ec_io18_write(EC_MSG_OFFSET_PARAM, did);
ec_io18_write(EC_MSG_OFFSET_CMD, EC_CMD_HWP_RD);
ret = imanager_check_ec_ready();
if (ret)
return -1;
ret = ec_io18_read(EC_MSG_OFFSET_STATUS);
if (ret != EC_F_SUCCESS)
return -1;
ret = imanager_check_ec_ready();
if (ret)
return -1;
UINT16 voltage = 0;
voltage = ec_io18_read(3 + 0);
voltage = voltage << 8;
voltage = voltage | ec_io18_read(3 + 1);
voltage = voltage * scaler;
voltage = voltage * 2933 / 1000;
return voltage;
}
//------------------------------------------------------------------------ec
EFI_STATUS EFIAPI PeriodicTimerDispatchHandler(EFI_HANDLE DispatchHandle, EFI_SMM_PERIODIC_TIMER_DISPATCH_CONTEXT* DispatchContext) //여기가 매인루틴 입니다.
{
RETURN_STATUS writeState;
UINTN SmBusAddress = 0;
cpuTemp = ec_read_ACPItemp_remote(1);
SmBusAddress = SmAddrMaker(0xD1, 0x72);
SmBusWriteDataByte(SmBusAddress, cpuTemp, &writeState);
systemTemp = ec_read_ACPItemp(1);
SmBusAddress = SmAddrMaker(0xD2, 0x72);
SmBusWriteDataByte(SmBusAddress, systemTemp, &writeState);
fanRPM1 = ec_read_ACPIfanspeed(0);
SmBusAddress = SmAddrMaker(0xD4, 0x72);
SmBusWriteDataByte(SmBusAddress, fanRPM1, &writeState);
SmBusAddress = SmAddrMaker(0xD5, 0x72);
SmBusWriteDataByte(SmBusAddress, fanRPM1 >> 8, &writeState);
fanRPM2 = ec_read_ACPIfanspeed(1);
SmBusAddress = SmAddrMaker(0xD6, 0x72);
SmBusWriteDataByte(SmBusAddress, fanRPM2, &writeState);
SmBusAddress = SmAddrMaker(0xD7, 0x72);
SmBusWriteDataByte(SmBusAddress, fanRPM2 >> 8, &writeState);
fanRPM3 = ec_read_ACPIfanspeed(2);
SmBusAddress = SmAddrMaker(0xD8, 0x72);
SmBusWriteDataByte(SmBusAddress, fanRPM3, &writeState);
SmBusAddress = SmAddrMaker(0xD9, 0x72);
SmBusWriteDataByte(SmBusAddress, fanRPM3 >> 8, &writeState);
vin12v = ec_read_ADC(100, 10);
SmBusAddress = SmAddrMaker(0xE1, 0x72);
SmBusWriteDataByte(SmBusAddress, vin12v, &writeState);
SmBusAddress = SmAddrMaker(0xE2, 0x72);
SmBusWriteDataByte(SmBusAddress, vin12v >> 8, &writeState);
vin5v = ec_read_ADC(87, 2);
SmBusAddress = SmAddrMaker(0xE3, 0x72);
SmBusWriteDataByte(SmBusAddress, vin5v, &writeState);
SmBusAddress = SmAddrMaker(0xE4, 0x72);
SmBusWriteDataByte(SmBusAddress, vin5v >> 8, &writeState);
vin5vsb = ec_read_ADC(90, 2);
SmBusAddress = SmAddrMaker(0xE5, 0x72);
SmBusWriteDataByte(SmBusAddress, vin5vsb, &writeState);
SmBusAddress = SmAddrMaker(0xE6, 0x72);
SmBusWriteDataByte(SmBusAddress, vin5vsb >> 8, &writeState);
vin33v = ec_read_ADC(93, 2);
SmBusAddress = SmAddrMaker(0xE7, 0x72);
SmBusWriteDataByte(SmBusAddress, vin33v, &writeState);
SmBusAddress = SmAddrMaker(0xE8, 0x72);
SmBusWriteDataByte(SmBusAddress, vin33v >> 8, &writeState);
vinbat = ec_read_ADC(81, 2);
SmBusAddress = SmAddrMaker(0xE9, 0x72);
SmBusWriteDataByte(SmBusAddress, vinbat, &writeState);
SmBusAddress = SmAddrMaker(0xEA, 0x72);
SmBusWriteDataByte(SmBusAddress, vinbat >> 8, &writeState);
return EFI_SUCCESS;
}
EFI_STATUS PeriodicTimerDispatchRegister(EFI_HANDLE* DispatchHandle)
{
EFI_STATUS Status = EFI_INVALID_PARAMETER;
if (m_PeriodicTimerDispatch)
{
// register periodic timer routine
Status = m_PeriodicTimerDispatch->Register(m_PeriodicTimerDispatch, PeriodicTimerDispatchHandler, &m_PeriodicTimerDispatch2RegCtx, DispatchHandle);
}
return Status;
}
EFI_STATUS PeriodicTimerDispatchUnregister(EFI_HANDLE DispatchHandle) //언레지스터
{
EFI_STATUS Status = EFI_INVALID_PARAMETER;
if (m_PeriodicTimerDispatch)
{
// register periodic timer routine
Status = m_PeriodicTimerDispatch->UnRegister(m_PeriodicTimerDispatch, DispatchHandle);
}
return Status;
}
EFI_STATUS EFIAPI PeriodicTimerDispatchProtocolNotifyHandler(EFI_EVENT Event, PVOID Context)
{
EFI_SMM_PERIODIC_TIMER_DISPATCH_PROTOCOL* PeriodicTimerDispatch = NULL;
gBS->LocateProtocol(&gEfiSmmPeriodicTimerDispatchProtocolGuid, NULL, &PeriodicTimerDispatch);
m_PeriodicTimerDispatch = PeriodicTimerDispatch;
return EFI_SUCCESS;
}
int memcmp(const void* s1, const void* s2, size_t n)
{
const unsigned char* p1 = s1, * p2 = s2;
while (n--)
if (*p1 != *p2)
return *p1 - *p2;
else
p1++, p2++;
return 0;
}
#define AMI_USB_SMM_PROTOCOL_GUID { 0x3ef7500e, 0xcf55, 0x474f, \
{ 0x8e, 0x7e, 0x00, 0x9e, 0x0e, 0xac, 0xec, 0xd2 }}
EFI_LOCATE_PROTOCOL old_SmmLocateProtocol = NULL;
EFI_STATUS EFIAPI new_SmmLocateProtocol(
EFI_GUID* Protocol,
VOID* Registration,
VOID** Interface)
{
EFI_GUID TargetGuid = AMI_USB_SMM_PROTOCOL_GUID;
/*
Totally board-specific hack for Intel DQ77KB, SmmLocateProtocol
with AMI_USB_SMM_PROTOCOL_GUID is calling during OS startup after
APIC init, so, here we can register our SMI timer.
*/
if (Protocol && !memcmp(Protocol, &TargetGuid, sizeof(TargetGuid)))
{
if (m_PeriodicTimerDispatchHandle)
{
// unregister previously registered timer
PeriodicTimerDispatchUnregister(m_PeriodicTimerDispatchHandle);
m_PeriodicTimerDispatchHandle = NULL;
}
// enable periodic timer SMI again
PeriodicTimerDispatchRegister(&m_PeriodicTimerDispatchHandle);
// remove the hook
gSmst->SmmLocateProtocol = old_SmmLocateProtocol;
}
return old_SmmLocateProtocol(Protocol, Registration, Interface);
}
EFI_STATUS RegisterProtocolNotifyDxe(
EFI_GUID* Guid, EFI_EVENT_NOTIFY Handler,
EFI_EVENT* Event, PVOID* Registration)
{
EFI_STATUS Status = gBS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, Handler, NULL, Event);
if (EFI_ERROR(Status))
{
return Status;
}
Status = gBS->RegisterProtocolNotify(Guid, *Event, (PVOID)Registration);
if (EFI_ERROR(Status))
{
return Status;
}
return Status;
}
VOID HmonEntrySmm(VOID)
{
PVOID Registration = NULL;
EFI_SMM_PERIODIC_TIMER_DISPATCH_PROTOCOL* PeriodicTimerDispatch = NULL;
EFI_STATUS Status = gBS->LocateProtocol(&gEfiSmmPeriodicTimerDispatchProtocolGuid, NULL, &PeriodicTimerDispatch);
if (Status == EFI_SUCCESS) {
// protocol is already present, call handler directly
m_PeriodicTimerDispatch = PeriodicTimerDispatch;
}
else
{
EFI_EVENT Event = NULL;
RegisterProtocolNotifyDxe(&gEfiSmmPeriodicTimerDispatchProtocolGuid, PeriodicTimerDispatchProtocolNotifyHandler, &Event, &Registration);
}
old_SmmLocateProtocol = gSmst->SmmLocateProtocol;
gSmst->SmmLocateProtocol = new_SmmLocateProtocol;
}
EFI_STATUS
HmonEntry(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE* SystemTable) {
gST = SystemTable;
gBS = gST->BootServices;
EFI_STATUS Status = EFI_SUCCESS;
BOOLEAN bInSmram = FALSE;
EFI_SMM_BASE2_PROTOCOL* SmmBase2 = NULL;
Status = gBS->LocateProtocol(&gEfiSmmBase2ProtocolGuid, NULL, &SmmBase2);
if (Status == EFI_SUCCESS) {
SmmBase2->InSmm(SmmBase2, &bInSmram);
}else{
}
if (bInSmram) {
Status = SmmBase2->GetSmstLocation(SmmBase2, &gSmst);
if (Status == EFI_SUCCESS){
HmonEntrySmm();
}
}
else {
}
return EFI_SUCCESS;
}
PCH에 내장되어 있는 smbus 컨트롤러를 다루는 코드는 EDK2 패키지와 함께 배포되는
인텔 쿼크를 위한 펌웨어 소스에 구현되어 있었고
같은 인텔 재품답게 큰 수정 없이 q87에서 작동하도록 할 수 있었습니다.
이 부분은 개같은 플레시 재부팅 노가다 없이 어플리캐이션 모드로 컴파일해서 태스트 할 수 있었기 때문에 금세 해결 할 수 있었습니다.
마찬가지로 it8518e 에서 정보를 가저 오는것도 어플리캐이션으로 컴파일해서 태스트 할 수 있었습니다.
그리고 q87에서 쏘는 SMBUS 정보를 받는 쪽은 라즈베리파이로 구성했습니다.
이번에는 과거와 달리 라즈베리 파이를 smbus 슬레이브로 작동 하도록 만들어야 했습니다.
https://raspberrypi.stackexchange.com/questions/76109/raspberry-as-an-i2c-slave
여기에서 C++로 만든 예제 코드 복붙한다음 살짝 수정해서
자료를 받아서 공유메모리를 통해서 파이선으로 만든 oled 재어 프로그램으로 넘겨주도록 구성했습니다.
#include <pigpio.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string>
#include <time.h>
using namespace std;
typedef struct _HmonData{
int cpuTemp;
int systemTemp;
int fanRPM1;
int fanRPM2;
int fanRPM3;
int vin12v;
int vin5v;
int vin5vsb;
int vin33v;
int vinbat;
int error;
}HmonData;
void runSlave();
void closeSlave();
int getControlBits(int, bool);
HmonData * Sdata= NULL;
int rastTime = 0;
int cpuTemp, systemTemp;
int vin12v, vin5v, vin5vsb, vin33v, vinbat;
int fanRPM1, fanRPM2, fanRPM3;
void* shard_memory = (void*)0;
int shmid;
const int slaveAddress = 0x72; // <-- Your address of choice
bsc_xfer_t xfer; // Struct to control data flow
time_t timert;
struct tm *tti;
int cpuID=0;
int main(){
timert = time(NULL);
tti = localtime(&timert);
//t->tm_sec;
// Chose one of those two lines (comment the other out):
int i;
void *shared_memory = (void *)0;
shmid = shmget((key_t)3888, sizeof(int)*11, 0666|IPC_CREAT);
if (shmid == -1){
printf("shmget failed \n");
}
shared_memory = shmat(shmid, (void *)0, 0);
if (shared_memory == (void *)-1)
{
printf("shmat failed : ");
exit(0);
}
Sdata = (HmonData*)shared_memory;
runSlave();
return 0;
}
void runSlave() {
gpioInitialise();
cout << "Initialized GPIOs\n";
// Close old device (if any)
xfer.control = getControlBits(slaveAddress, false); // To avoid conflicts when restarting
bscXfer(&xfer);
// Set I2C slave Address to 0x0A
xfer.control = getControlBits(slaveAddress, true);
int status = bscXfer(&xfer); // Should now be visible in I2C-Scanners
if (status >= 0)
{
cout << "Opened slave\n";
xfer.rxCnt = 0;
while(1){
bscXfer(&xfer);
if(xfer.rxCnt > 0) {
Sdata->error = 0;
switch(xfer.rxBuf[0]){
case 0xD1:
rastTime = time(0);
cpuTemp = xfer.rxBuf[1];
//printf("cpuTemp is %d \n", cpuTemp);
break;
case 0xD2:
systemTemp = xfer.rxBuf[1];
//printf("systemTEMP is %d in decimal.\n", systemTemp);
break;
case 0xD4:
fanRPM1 = xfer.rxBuf[3];
fanRPM1 = fanRPM1 << 8;
fanRPM1 = fanRPM1 + xfer.rxBuf[1];
printf("FanRpm is %d in decimal.\n", fanRPM1);
break;
case 0xD6:
fanRPM2 = xfer.rxBuf[3];
fanRPM2 = fanRPM2 << 8;
fanRPM2 = fanRPM2 + xfer.rxBuf[1];
break;
case 0xD8:
fanRPM3 = xfer.rxBuf[3];
fanRPM3 = fanRPM3 << 8;
fanRPM3 = fanRPM3 + xfer.rxBuf[1];
break;
case 0xE1:
vin12v = xfer.rxBuf[3];
vin12v = vin12v << 8;
vin12v = vin12v + xfer.rxBuf[1];
break;
case 0xE3:
vin5v = xfer.rxBuf[3];
vin5v = vin5v << 8;
vin5v = vin5v + xfer.rxBuf[1];
break;
case 0xE5:
vin5vsb = xfer.rxBuf[3];
vin5vsb = vin5vsb << 8;
vin5vsb = vin5vsb + xfer.rxBuf[1];
break;
case 0xE7:
vin33v = xfer.rxBuf[3];
vin33v = vin33v << 8;
vin33v = vin33v + xfer.rxBuf[1];
break;
case 0xE9:
vinbat = xfer.rxBuf[3];
vinbat = vinbat << 8;
vinbat = vinbat + xfer.rxBuf[1];
break;
}
Sdata->cpuTemp = cpuTemp;
Sdata->systemTemp = systemTemp;
Sdata->fanRPM1 = fanRPM1;
Sdata->fanRPM2 = fanRPM2;
Sdata->fanRPM3 = fanRPM3;
Sdata->vin12v = vin12v;
Sdata->vin5v = vin5v;
Sdata->vin33v = vin33v;
Sdata->vin5vsb = vin5vsb;
Sdata->vinbat = vinbat;
//printf("12v voltage %d in decimal.\n", Sdata->vin12v);
memset( xfer.rxBuf, '\0', BSC_FIFO_SIZE );
//for(int i = 0; i < xfer.rxCnt; i++){
// cout.width(2);
// cout.fill('0');
// cout <<hex<<(int)xfer.rxBuf[i];
//}
}
if(time(0) - rastTime>2 || Sdata->error == 1){
Sdata->error = 1;
Sdata->cpuTemp = 0;
Sdata->systemTemp = 0;
Sdata->fanRPM1 = 0;
Sdata->fanRPM2 = 0;
Sdata->fanRPM3 = 0;
Sdata->vin12v = 0;
Sdata->vin5v = 0;
Sdata->vin33v = 0;
Sdata->vin5vsb = 0;
Sdata->vinbat = 0;
}
//if (xfer.rxCnt > 0){
// cout << xfer.rxBuf;
//}
}
}else cout << "Failed to open slave!!!\n";
}
}
void closeSlave() {
gpioInitialise();
cout << "Initialized GPIOs\n";
xfer.control = getControlBits(slaveAddress, false);
bscXfer(&xfer);
cout << "Closed slave.\n";
gpioTerminate();
cout << "Terminated GPIOs.\n";
shmctl(shmid, IPC_RMID, 0);
}
int getControlBits(int address /* max 127 */, bool open) {
/*
Excerpt from http://abyz.me.uk/rpi/pigpio/cif.html#bscXfer regarding the control bits:
22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
a a a a a a a - - IT HC TF IR RE TE BK EC ES PL PH I2 SP EN
Bits 0-13 are copied unchanged to the BSC CR register. See pages 163-165 of the Broadcom
peripherals document for full details.
aaaaaaa defines the I2C slave address (only relevant in I2C mode)
IT invert transmit status flags
HC enable host control
TF enable test FIFO
IR invert receive status flags
RE enable receive
TE enable transmit
BK abort operation and clear FIFOs
EC send control register as first I2C byte
ES send status register as first I2C byte
PL set SPI polarity high
PH set SPI phase high
I2 enable I2C mode
SP enable SPI mode
EN enable BSC peripheral
*/
// Flags like this: 0b/*IT:*/0/*HC:*/0/*TF:*/0/*IR:*/0/*RE:*/0/*TE:*/0/*BK:*/0/*EC:*/0/*ES:*/0/*PL:*/0/*PH:*/0/*I2:*/0/*SP:*/0/*EN:*/0;
int flags;
if(open)
flags = /*RE:*/ (1 << 9) | /*TE:*/ (1 << 8) | /*I2:*/ (1 << 2) | /*EN:*/ (1 << 0);
else // Close/Abort
flags = /*BK:*/ (1 << 7) | /*I2:*/ (0 << 2) | /*EN:*/ (0 << 0);
return (address << 16 /*= to the start of significant bits*/) | flags;
}
완료후 알게된것.
SMM은 아주 막강한 기능을 가지고 있습니다만. 이걸로 기능을 구현하는 길은 날이 가면 갈수록
힘들어 지고 있습니다.
과거의 SMM은 어떤 인터럽트로 진입하더라도 모든 코어가 동시에 SMM으로 묶이는 탓에 성능손해가 막심했다고
±â´ÉÀÌ ÀÖ´Ù Á¤µµ¸¸ ÀÌÇØÇ߳׿ä.
ÀÌ·±±â´ÉÀÌ ÀÖ´Â°Ç Ã· ¾Ë¾Ò³×¿ä.
¾î·Æ½À´Ï´Ù¤Ì
ÀÌ°Ô Áö³ªÄ¡°Ô º¸µå¸¶´Ù »óȲÀÌ ´Þ¶ó¼ ü°èÀûÀÎ ÁöħÀ¸·Î ¸¸µå´Â°Ç ¹«¸®°¡ ÀÖ°í..
³ªÁß¿¡ ´Ù½Ã ºÃÀ»¶§ ³°¨ÇÏÁö ¾Êµµ·Ï ±â·ÏÇÏ´Â Á¤µµÀÇ ÀÇÀǸ¦ °¡Áö°í ÀûÀº ÈıⰡ µÇ¾î º·È´Âµ¥..
±×·¡µµ ÀоîÁֽô ºÐÀÌ °è¼Ì³×¿ä..
»ý´ÉÀ» °í·ÁÇÏ¸é ¾û¸ÁÁøâÀÎ ±¸ÇöÀ̶ó..
Àú º¸µå°¡ Çѹøµµ UEFI ¾÷µ¥ÀÌÆ®¶ó´ø°¡ ÇÑ ÀûÀÌ ¾ø´õ¶ó°í¿ä...
Æò¹üÇÏ°Ô ¸®Å×ÀÏ ¹°·®µµ ³ª°¡´Â ȸ»ç¸é Æß¾÷ ¾ø¾î¿ä? ÇÏ°í ¸ÅÀÏÀÌ¶óµµ º¸³» ºÃÀ»ÅÙµ¥...
¾Æ¿¹ ±×·± Àå»ç¸¦ ¾ÈÇϴ ȸ»ê°Í °°¾Æ¼ ¾ÈÇØ ºÃ°Åµç¿ä...
'ÀÀ ³×°¡ ¿Ö ±×°É °¡Áö°í ÀÖ¾î¿ä? ' °°Àº ¹ÝÀÀÀÌ ¿Ã°Í°°¾Æ¼¿ä..
¹°·Ð, µµÀúÈ÷ ÀÌÇظ¦ ¸øÇÏÁö¸¸ ¸»ÀÔ´Ï´Ù. ¤Ñ¤Ñ;
±×·¡µµ ´©±º°¡´Â ²À ÇÊ¿äÇÑ Á¤º¸°¡ ¾Æ´Ò±î »ý°¢Çغ¾´Ï´Ù.^^
´Ù¸¥ »ç¶÷ÀÌ ´Ù¸¥ º¸µå·Î ÇÏ·Á¸é ¶Ç ºñ½ÁÇÑ »ðÁúÀÌ ÇÊ¿äÇÒ °Ì´Ï´Ù.
PCHµµ ´Ù¸£¸é ´Þ¶óÁö´Â ºÎºÐÀÌ ÀÖ°í superio Ĩ¼ÂÀÌ ´Ù¸£¸é ¶Ç ´Þ¶óÁö´Â °ÍÀÌ ÀÖ°í ±×·¡¼¿ä..
±×³É ÀÌ·±°Ô µÈ´Ù ¼öÁØÀÇ À̾߱Ⱑ µÉ ¼ö ¹Û¿¡ ¾ø´õ¶ó°í¿ä.. ±×·¡µµ Ȥ½Ã³ª ½Í¾î¼ ÷ºÎ Çß½À´Ï´Ù ÂÁ..