1ece92f85SJason Jin /**************************************************************************** 2ece92f85SJason Jin * 3ece92f85SJason Jin * BIOS emulator and interface 4ece92f85SJason Jin * to Realmode X86 Emulator Library 5ece92f85SJason Jin * 6ece92f85SJason Jin * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. 7ece92f85SJason Jin * Jason Jin <Jason.jin@freescale.com> 8ece92f85SJason Jin * 9ece92f85SJason Jin * Copyright (C) 1996-1999 SciTech Software, Inc. 10ece92f85SJason Jin * 11ece92f85SJason Jin * ======================================================================== 12ece92f85SJason Jin * 13ece92f85SJason Jin * Permission to use, copy, modify, distribute, and sell this software and 14ece92f85SJason Jin * its documentation for any purpose is hereby granted without fee, 15ece92f85SJason Jin * provided that the above copyright notice appear in all copies and that 16ece92f85SJason Jin * both that copyright notice and this permission notice appear in 17ece92f85SJason Jin * supporting documentation, and that the name of the authors not be used 18ece92f85SJason Jin * in advertising or publicity pertaining to distribution of the software 19ece92f85SJason Jin * without specific, written prior permission. The authors makes no 20ece92f85SJason Jin * representations about the suitability of this software for any purpose. 21ece92f85SJason Jin * It is provided "as is" without express or implied warranty. 22ece92f85SJason Jin * 23ece92f85SJason Jin * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 24ece92f85SJason Jin * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 25ece92f85SJason Jin * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 26ece92f85SJason Jin * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 27ece92f85SJason Jin * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 28ece92f85SJason Jin * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 29ece92f85SJason Jin * PERFORMANCE OF THIS SOFTWARE. 30ece92f85SJason Jin * 31ece92f85SJason Jin * ======================================================================== 32ece92f85SJason Jin * 33ece92f85SJason Jin * Language: ANSI C 34ece92f85SJason Jin * Environment: Any 35ece92f85SJason Jin * Developer: Kendall Bennett 36ece92f85SJason Jin * 37ece92f85SJason Jin * Description: Module implementing the system specific functions. This 38ece92f85SJason Jin * module is always compiled and linked in the OS depedent 39ece92f85SJason Jin * libraries, and never in a binary portable driver. 40ece92f85SJason Jin * 41ece92f85SJason Jin * Jason ported this file to u-boot to run the ATI video card BIOS 42ece92f85SJason Jin * in u-boot. Made all the video memory be emulated during the 43ece92f85SJason Jin * BIOS runing process which may affect the VGA function but the 44ece92f85SJason Jin * frambuffer function can work after run the BIOS. 45ece92f85SJason Jin * 46ece92f85SJason Jin ****************************************************************************/ 47ece92f85SJason Jin 48ece92f85SJason Jin #include <malloc.h> 4978cff50eSMichal Simek #include <common.h> 50ece92f85SJason Jin 51ce981dc8SJason Jin #if defined(CONFIG_BIOSEMU) 52ce981dc8SJason Jin 535b4de930SMichal Simek #include "biosemui.h" 545b4de930SMichal Simek 55ece92f85SJason Jin BE_sysEnv _BE_env = {{0}}; 56ece92f85SJason Jin static X86EMU_memFuncs _BE_mem __attribute__((section(".got2"))) = { 57ece92f85SJason Jin BE_rdb, 58ece92f85SJason Jin BE_rdw, 59ece92f85SJason Jin BE_rdl, 60ece92f85SJason Jin BE_wrb, 61ece92f85SJason Jin BE_wrw, 62ece92f85SJason Jin BE_wrl, 63ece92f85SJason Jin }; 64ece92f85SJason Jin 65ece92f85SJason Jin static X86EMU_pioFuncs _BE_pio __attribute__((section(".got2"))) = { 66ece92f85SJason Jin BE_inb, 67ece92f85SJason Jin BE_inw, 68ece92f85SJason Jin BE_inl, 69ece92f85SJason Jin BE_outb, 70ece92f85SJason Jin BE_outw, 71ece92f85SJason Jin BE_outl, 72ece92f85SJason Jin }; 73ece92f85SJason Jin 74ece92f85SJason Jin #define OFF(addr) (u16)(((addr) >> 0) & 0xffff) 75ece92f85SJason Jin #define SEG(addr) (u16)(((addr) >> 4) & 0xf000) 76ece92f85SJason Jin 77ece92f85SJason Jin /**************************************************************************** 78ece92f85SJason Jin PARAMETERS: 79ece92f85SJason Jin debugFlags - Flags to enable debugging options (debug builds only) 80ece92f85SJason Jin memSize - Amount of memory to allocate for real mode machine 81ece92f85SJason Jin info - Pointer to default VGA device information 82ece92f85SJason Jin 83ece92f85SJason Jin REMARKS: 84ece92f85SJason Jin This functions initialises the BElib, and uses the passed in 85ece92f85SJason Jin BIOS image as the BIOS that is used and emulated at 0xC0000. 86ece92f85SJason Jin ****************************************************************************/ 87ece92f85SJason Jin int X86API BE_init(u32 debugFlags, int memSize, BE_VGAInfo * info, int shared) 88ece92f85SJason Jin { 89ece92f85SJason Jin #if !defined(__DRIVER__) && !defined(__KERNEL__) 90ece92f85SJason Jin 91ece92f85SJason Jin PM_init(); 92ece92f85SJason Jin #endif 93ece92f85SJason Jin memset(&M, 0, sizeof(M)); 94ece92f85SJason Jin if (memSize < 20480){ 95ece92f85SJason Jin printf("Emulator requires at least 20Kb of memory!\n"); 96ece92f85SJason Jin return 0; 97ece92f85SJason Jin } 98ece92f85SJason Jin 99*409ecdc0SWolfgang Denk M.mem_base = malloc(memSize); 100ece92f85SJason Jin 101ece92f85SJason Jin if (M.mem_base == NULL){ 102ece92f85SJason Jin printf("Biosemu:Out of memory!"); 103ece92f85SJason Jin return 0; 104ece92f85SJason Jin } 105ece92f85SJason Jin M.mem_size = memSize; 106ece92f85SJason Jin 107ece92f85SJason Jin _BE_env.emulateVGA = 0; 108ece92f85SJason Jin _BE_env.busmem_base = (unsigned long)malloc(128 * 1024); 109*409ecdc0SWolfgang Denk if ((void *)_BE_env.busmem_base == NULL){ 110ece92f85SJason Jin printf("Biosemu:Out of memory!"); 111ece92f85SJason Jin return 0; 112ece92f85SJason Jin } 113ece92f85SJason Jin M.x86.debug = debugFlags; 114ece92f85SJason Jin _BE_bios_init((u32*)info->LowMem); 115ece92f85SJason Jin X86EMU_setupMemFuncs(&_BE_mem); 116ece92f85SJason Jin X86EMU_setupPioFuncs(&_BE_pio); 117ece92f85SJason Jin BE_setVGA(info); 118ece92f85SJason Jin return 1; 119ece92f85SJason Jin } 120ece92f85SJason Jin 121ece92f85SJason Jin /**************************************************************************** 122ece92f85SJason Jin PARAMETERS: 123ece92f85SJason Jin info - Pointer to VGA device information to make current 124ece92f85SJason Jin 125ece92f85SJason Jin REMARKS: 126ece92f85SJason Jin This function sets the VGA BIOS functions in the emulator to point to the 127ece92f85SJason Jin specific VGA BIOS in use. This includes swapping the BIOS interrupt 128ece92f85SJason Jin vectors, BIOS image and BIOS data area to the new BIOS. This allows the 129ece92f85SJason Jin real mode BIOS to be swapped without resetting the entire emulator. 130ece92f85SJason Jin ****************************************************************************/ 131ece92f85SJason Jin void X86API BE_setVGA(BE_VGAInfo * info) 132ece92f85SJason Jin { 133ece92f85SJason Jin 134ece92f85SJason Jin #ifdef __KERNEL__ 135ece92f85SJason Jin _BE_env.vgaInfo.function = info->function; 136ece92f85SJason Jin _BE_env.vgaInfo.device = info->device; 137ece92f85SJason Jin _BE_env.vgaInfo.bus = info->bus; 138ece92f85SJason Jin _BE_env.vgaInfo.pcidev = info->pcidev; 139ece92f85SJason Jin #else 140ece92f85SJason Jin _BE_env.vgaInfo.pciInfo = info->pciInfo; 141ece92f85SJason Jin #endif 142ece92f85SJason Jin _BE_env.vgaInfo.BIOSImage = info->BIOSImage; 143ece92f85SJason Jin if (info->BIOSImage) { 144ece92f85SJason Jin _BE_env.biosmem_base = (ulong) info->BIOSImage; 145ece92f85SJason Jin _BE_env.biosmem_limit = 0xC0000 + info->BIOSImageLen - 1; 146ece92f85SJason Jin } else { 147ece92f85SJason Jin _BE_env.biosmem_base = _BE_env.busmem_base + 0x20000; 148ece92f85SJason Jin _BE_env.biosmem_limit = 0xC7FFF; 149ece92f85SJason Jin } 150ece92f85SJason Jin if (*((u32 *) info->LowMem) == 0) 151ece92f85SJason Jin _BE_bios_init((u32 *) info->LowMem); 152ece92f85SJason Jin memcpy((u8 *) M.mem_base, info->LowMem, sizeof(info->LowMem)); 153ece92f85SJason Jin } 154ece92f85SJason Jin 155ece92f85SJason Jin /**************************************************************************** 156ece92f85SJason Jin PARAMETERS: 157ece92f85SJason Jin info - Pointer to VGA device information to retrieve current 158ece92f85SJason Jin 159ece92f85SJason Jin REMARKS: 160ece92f85SJason Jin This function returns the VGA BIOS functions currently active in the 161ece92f85SJason Jin emulator, so they can be restored at a later date. 162ece92f85SJason Jin ****************************************************************************/ 163ece92f85SJason Jin void X86API BE_getVGA(BE_VGAInfo * info) 164ece92f85SJason Jin { 165ece92f85SJason Jin #ifdef __KERNEL__ 166ece92f85SJason Jin info->function = _BE_env.vgaInfo.function; 167ece92f85SJason Jin info->device = _BE_env.vgaInfo.device; 168ece92f85SJason Jin info->bus = _BE_env.vgaInfo.bus; 169ece92f85SJason Jin info->pcidev = _BE_env.vgaInfo.pcidev; 170ece92f85SJason Jin #else 171ece92f85SJason Jin info->pciInfo = _BE_env.vgaInfo.pciInfo; 172ece92f85SJason Jin #endif 173ece92f85SJason Jin info->BIOSImage = _BE_env.vgaInfo.BIOSImage; 174ece92f85SJason Jin memcpy(info->LowMem, (u8 *) M.mem_base, sizeof(info->LowMem)); 175ece92f85SJason Jin } 176ece92f85SJason Jin 177ece92f85SJason Jin /**************************************************************************** 178ece92f85SJason Jin PARAMETERS: 179ece92f85SJason Jin r_seg - Segment for pointer to convert 180ece92f85SJason Jin r_off - Offset for pointer to convert 181ece92f85SJason Jin 182ece92f85SJason Jin REMARKS: 183ece92f85SJason Jin This function maps a real mode pointer in the emulator memory to a protected 184ece92f85SJason Jin mode pointer that can be used to directly access the memory. 185ece92f85SJason Jin 186ece92f85SJason Jin NOTE: The memory is *always* in little endian format, son on non-x86 187ece92f85SJason Jin systems you will need to do endian translations to access this 188ece92f85SJason Jin memory. 189ece92f85SJason Jin ****************************************************************************/ 190ece92f85SJason Jin void *X86API BE_mapRealPointer(uint r_seg, uint r_off) 191ece92f85SJason Jin { 192ece92f85SJason Jin u32 addr = ((u32) r_seg << 4) + r_off; 193ece92f85SJason Jin 194ece92f85SJason Jin if (addr >= 0xC0000 && addr <= _BE_env.biosmem_limit) { 195ece92f85SJason Jin return (void *)(_BE_env.biosmem_base + addr - 0xC0000); 196ece92f85SJason Jin } else if (addr >= 0xA0000 && addr <= 0xFFFFF) { 197ece92f85SJason Jin return (void *)(_BE_env.busmem_base + addr - 0xA0000); 198ece92f85SJason Jin } 199ece92f85SJason Jin return (void *)(M.mem_base + addr); 200ece92f85SJason Jin } 201ece92f85SJason Jin 202ece92f85SJason Jin /**************************************************************************** 203ece92f85SJason Jin PARAMETERS: 204ece92f85SJason Jin len - Return the length of the VESA buffer 205ece92f85SJason Jin rseg - Place to store VESA buffer segment 206ece92f85SJason Jin roff - Place to store VESA buffer offset 207ece92f85SJason Jin 208ece92f85SJason Jin REMARKS: 209ece92f85SJason Jin This function returns the address of the VESA transfer buffer in real 210ece92f85SJason Jin _BE_piomode emulator memory. The VESA transfer buffer is always 1024 bytes long, 211ece92f85SJason Jin and located at 15Kb into the start of the real mode memory (16Kb is where 212ece92f85SJason Jin we put the real mode code we execute for issuing interrupts). 213ece92f85SJason Jin 214ece92f85SJason Jin NOTE: The memory is *always* in little endian format, son on non-x86 215ece92f85SJason Jin systems you will need to do endian translations to access this 216ece92f85SJason Jin memory. 217ece92f85SJason Jin ****************************************************************************/ 218ece92f85SJason Jin void *X86API BE_getVESABuf(uint * len, uint * rseg, uint * roff) 219ece92f85SJason Jin { 220ece92f85SJason Jin *len = 1024; 221ece92f85SJason Jin *rseg = SEG(0x03C00); 222ece92f85SJason Jin *roff = OFF(0x03C00); 223ece92f85SJason Jin return (void *)(M.mem_base + ((u32) * rseg << 4) + *roff); 224ece92f85SJason Jin } 225ece92f85SJason Jin 226ece92f85SJason Jin /**************************************************************************** 227ece92f85SJason Jin REMARKS: 228ece92f85SJason Jin Cleans up and exits the emulator. 229ece92f85SJason Jin ****************************************************************************/ 230ece92f85SJason Jin void X86API BE_exit(void) 231ece92f85SJason Jin { 232ece92f85SJason Jin free(M.mem_base); 233*409ecdc0SWolfgang Denk free((void *)_BE_env.busmem_base); 234ece92f85SJason Jin } 235ece92f85SJason Jin 236ece92f85SJason Jin /**************************************************************************** 237ece92f85SJason Jin PARAMETERS: 238ece92f85SJason Jin seg - Segment of code to call 239ece92f85SJason Jin off - Offset of code to call 240ece92f85SJason Jin regs - Real mode registers to load 241ece92f85SJason Jin sregs - Real mode segment registers to load 242ece92f85SJason Jin 243ece92f85SJason Jin REMARKS: 244ece92f85SJason Jin This functions calls a real mode far function at the specified address, 245ece92f85SJason Jin and loads all the x86 registers from the passed in registers structure. 246ece92f85SJason Jin On exit the registers returned from the call are returned in the same 247ece92f85SJason Jin structures. 248ece92f85SJason Jin ****************************************************************************/ 249ece92f85SJason Jin void X86API BE_callRealMode(uint seg, uint off, RMREGS * regs, RMSREGS * sregs) 250ece92f85SJason Jin { 251ece92f85SJason Jin M.x86.R_EAX = regs->e.eax; 252ece92f85SJason Jin M.x86.R_EBX = regs->e.ebx; 253ece92f85SJason Jin M.x86.R_ECX = regs->e.ecx; 254ece92f85SJason Jin M.x86.R_EDX = regs->e.edx; 255ece92f85SJason Jin M.x86.R_ESI = regs->e.esi; 256ece92f85SJason Jin M.x86.R_EDI = regs->e.edi; 257ece92f85SJason Jin M.x86.R_DS = sregs->ds; 258ece92f85SJason Jin M.x86.R_ES = sregs->es; 259ece92f85SJason Jin M.x86.R_FS = sregs->fs; 260ece92f85SJason Jin M.x86.R_GS = sregs->gs; 261ece92f85SJason Jin 262ece92f85SJason Jin ((u8 *) M.mem_base)[0x4000] = 0x9A; 263ece92f85SJason Jin ((u8 *) M.mem_base)[0x4001] = (u8) off; 264ece92f85SJason Jin ((u8 *) M.mem_base)[0x4002] = (u8) (off >> 8); 265ece92f85SJason Jin ((u8 *) M.mem_base)[0x4003] = (u8) seg; 266ece92f85SJason Jin ((u8 *) M.mem_base)[0x4004] = (u8) (seg >> 8); 267ece92f85SJason Jin ((u8 *) M.mem_base)[0x4005] = 0xF1; /* Illegal op-code */ 268ece92f85SJason Jin M.x86.R_CS = SEG(0x04000); 269ece92f85SJason Jin M.x86.R_IP = OFF(0x04000); 270ece92f85SJason Jin 271ece92f85SJason Jin M.x86.R_SS = SEG(M.mem_size - 2); 272ece92f85SJason Jin M.x86.R_SP = OFF(M.mem_size - 2) + 2; 273ece92f85SJason Jin 274ece92f85SJason Jin X86EMU_exec(); 275ece92f85SJason Jin 276ece92f85SJason Jin regs->e.cflag = M.x86.R_EFLG & F_CF; 277ece92f85SJason Jin regs->e.eax = M.x86.R_EAX; 278ece92f85SJason Jin regs->e.ebx = M.x86.R_EBX; 279ece92f85SJason Jin regs->e.ecx = M.x86.R_ECX; 280ece92f85SJason Jin regs->e.edx = M.x86.R_EDX; 281ece92f85SJason Jin regs->e.esi = M.x86.R_ESI; 282ece92f85SJason Jin regs->e.edi = M.x86.R_EDI; 283ece92f85SJason Jin sregs->ds = M.x86.R_DS; 284ece92f85SJason Jin sregs->es = M.x86.R_ES; 285ece92f85SJason Jin sregs->fs = M.x86.R_FS; 286ece92f85SJason Jin sregs->gs = M.x86.R_GS; 287ece92f85SJason Jin } 288ece92f85SJason Jin 289ece92f85SJason Jin /**************************************************************************** 290ece92f85SJason Jin PARAMETERS: 291ece92f85SJason Jin intno - Interrupt number to execute 292ece92f85SJason Jin in - Real mode registers to load 293ece92f85SJason Jin out - Place to store resulting real mode registers 294ece92f85SJason Jin 295ece92f85SJason Jin REMARKS: 296ece92f85SJason Jin This functions calls a real mode interrupt function at the specified address, 297ece92f85SJason Jin and loads all the x86 registers from the passed in registers structure. 298ece92f85SJason Jin On exit the registers returned from the call are returned in out stucture. 299ece92f85SJason Jin ****************************************************************************/ 300ece92f85SJason Jin int X86API BE_int86(int intno, RMREGS * in, RMREGS * out) 301ece92f85SJason Jin { 302ece92f85SJason Jin M.x86.R_EAX = in->e.eax; 303ece92f85SJason Jin M.x86.R_EBX = in->e.ebx; 304ece92f85SJason Jin M.x86.R_ECX = in->e.ecx; 305ece92f85SJason Jin M.x86.R_EDX = in->e.edx; 306ece92f85SJason Jin M.x86.R_ESI = in->e.esi; 307ece92f85SJason Jin M.x86.R_EDI = in->e.edi; 308ece92f85SJason Jin ((u8 *) M.mem_base)[0x4000] = 0xCD; 309ece92f85SJason Jin ((u8 *) M.mem_base)[0x4001] = (u8) intno; 310ece92f85SJason Jin ((u8 *) M.mem_base)[0x4002] = 0xF1; 311ece92f85SJason Jin M.x86.R_CS = SEG(0x04000); 312ece92f85SJason Jin M.x86.R_IP = OFF(0x04000); 313ece92f85SJason Jin 314ece92f85SJason Jin M.x86.R_SS = SEG(M.mem_size - 1); 315ece92f85SJason Jin M.x86.R_SP = OFF(M.mem_size - 1) - 1; 316ece92f85SJason Jin 317ece92f85SJason Jin X86EMU_exec(); 318ece92f85SJason Jin out->e.cflag = M.x86.R_EFLG & F_CF; 319ece92f85SJason Jin out->e.eax = M.x86.R_EAX; 320ece92f85SJason Jin out->e.ebx = M.x86.R_EBX; 321ece92f85SJason Jin out->e.ecx = M.x86.R_ECX; 322ece92f85SJason Jin out->e.edx = M.x86.R_EDX; 323ece92f85SJason Jin out->e.esi = M.x86.R_ESI; 324ece92f85SJason Jin out->e.edi = M.x86.R_EDI; 325ece92f85SJason Jin return out->x.ax; 326ece92f85SJason Jin } 327ece92f85SJason Jin 328ece92f85SJason Jin /**************************************************************************** 329ece92f85SJason Jin PARAMETERS: 330ece92f85SJason Jin intno - Interrupt number to execute 331ece92f85SJason Jin in - Real mode registers to load 332ece92f85SJason Jin out - Place to store resulting real mode registers 333ece92f85SJason Jin sregs - Real mode segment registers to load 334ece92f85SJason Jin 335ece92f85SJason Jin REMARKS: 336ece92f85SJason Jin This functions calls a real mode interrupt function at the specified address, 337ece92f85SJason Jin and loads all the x86 registers from the passed in registers structure. 338ece92f85SJason Jin On exit the registers returned from the call are returned in out stucture. 339ece92f85SJason Jin ****************************************************************************/ 340ece92f85SJason Jin int X86API BE_int86x(int intno, RMREGS * in, RMREGS * out, RMSREGS * sregs) 341ece92f85SJason Jin { 342ece92f85SJason Jin M.x86.R_EAX = in->e.eax; 343ece92f85SJason Jin M.x86.R_EBX = in->e.ebx; 344ece92f85SJason Jin M.x86.R_ECX = in->e.ecx; 345ece92f85SJason Jin M.x86.R_EDX = in->e.edx; 346ece92f85SJason Jin M.x86.R_ESI = in->e.esi; 347ece92f85SJason Jin M.x86.R_EDI = in->e.edi; 348ece92f85SJason Jin M.x86.R_DS = sregs->ds; 349ece92f85SJason Jin M.x86.R_ES = sregs->es; 350ece92f85SJason Jin M.x86.R_FS = sregs->fs; 351ece92f85SJason Jin M.x86.R_GS = sregs->gs; 352ece92f85SJason Jin ((u8 *) M.mem_base)[0x4000] = 0xCD; 353ece92f85SJason Jin ((u8 *) M.mem_base)[0x4001] = (u8) intno; 354ece92f85SJason Jin ((u8 *) M.mem_base)[0x4002] = 0xF1; 355ece92f85SJason Jin M.x86.R_CS = SEG(0x04000); 356ece92f85SJason Jin M.x86.R_IP = OFF(0x04000); 357ece92f85SJason Jin 358ece92f85SJason Jin M.x86.R_SS = SEG(M.mem_size - 1); 359ece92f85SJason Jin M.x86.R_SP = OFF(M.mem_size - 1) - 1; 360ece92f85SJason Jin 361ece92f85SJason Jin X86EMU_exec(); 362ece92f85SJason Jin out->e.cflag = M.x86.R_EFLG & F_CF; 363ece92f85SJason Jin out->e.eax = M.x86.R_EAX; 364ece92f85SJason Jin out->e.ebx = M.x86.R_EBX; 365ece92f85SJason Jin out->e.ecx = M.x86.R_ECX; 366ece92f85SJason Jin out->e.edx = M.x86.R_EDX; 367ece92f85SJason Jin out->e.esi = M.x86.R_ESI; 368ece92f85SJason Jin out->e.edi = M.x86.R_EDI; 369ece92f85SJason Jin sregs->ds = M.x86.R_DS; 370ece92f85SJason Jin sregs->es = M.x86.R_ES; 371ece92f85SJason Jin sregs->fs = M.x86.R_FS; 372ece92f85SJason Jin sregs->gs = M.x86.R_GS; 373ece92f85SJason Jin return out->x.ax; 374ece92f85SJason Jin } 375ce981dc8SJason Jin #endif 376