kernel移植——从三星官方内核开始移植

以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除。

一、内核移植初体验

1、获取三星官方的内核源码包

三星SMDKV210开发板附带的光盘里有内核源码包:下载地址。

2、构建移植环境

(1)Windows下建立SI工程(用来分析)

  • 为了简单与方便,建立SI工程前先删除不必要的目录和文件,包括arch目录下非arm架构的目录文件,以及arch/arm目录下不是三星的mach-xxx、plat-xxx目录。

(2)在ubuntu下解压一份源码包(根据分析进行修改)

3、内核配置与编译

(1)在主Makefile文件的193行之后添加以下内容:

#added by xjh 
ARCH = arm
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-

(2)执行“make smdkv210_android_defconfig”。

  • 在arch/arm/configs目录下有很多配置文件,我们从中找到与三星相关的、与我们开发板最像的“smdkv210_android_defconfig”。

(3)执行“make menuconfig”。

  • 忽略弹出的配置界面,因为目前还不知道修整哪些内容。

(4)执行“make -j4”。

  • 直接执行make则会直接单线程编译,如果make -j4则会4线程编译,编译效率会提高。
  • 多少线程编译则要看是多少核的cpu:四核则可以4线程,双核则2线程。

4、通过tftp将编译得到的zImage下载到开发板

  • 编译得到的内核镜像在arch/arm/boot目录下。
  • 通过tftp将镜像下载到开发板的方法见博客:利用tftp将镜像下载到开发板_天糊土的博客

5、运行结果与分析

(1)运行结果

没有显示linux自解压代码打印出的“Uncompressing Linux... done, booting the kernel.”。


(2)现象分析

1)这个现象说明zImage没有被解压成功,因此问题出在解压相关的部分。

2)进一步分析,问题可能与“内核解压后的代码放在哪个内存地址处(暂且叫“解压地址”)”有关。这个解压地址可以由内核配置,如果不对,则内核不能运行。

3)内核的链接地址是一个虚拟地址,而自解压代码解压内核时需要物理地址(即解压地址是物理地址,因为此时mmu还没开,内核还没有开始运行),因此链接地址对应的物理地址必须等于解压地址,否则自解压之后内核无法运行。

4)因此现在问题变成:内核的链接等于多少?内核配置的解压地址是多少?

5)内核的链接地址与对应的物理地址,可以在arch/arm/kernel/head.S中可以查到。

PAGE_OFFSET这个宏定义在/arch/arm/include/asm/memory.h文件中,内容如下。

#define PAGE_OFFSET		UL(CONFIG_PAGE_OFFSET)

而在配置内核时生成的.config文件中,CONFIG_PAGE_OFFSET赋值为0xC0000000。

CONFIG_PAGE_OFFSET=0xC0000000

所以PAGE_OFFSET=0xC000_0000。

在/arch/arm/mach-s5pv210/include/mach/memory.h文件中,对PHYS_OFFSET这个宏有如下定义:

根据下面“(5)现象分析”,PHYS_OFFSET这个宏的值应该为0x30000000。

而TEXT_OFFSET这个宏的值应该为0x00008000。(待求证)

经上述分析得知,内核的链接地址KERNEL_RAM_VADDR为0xC0008000,对应的物理地址KERNEL_RAM_PADDR是0x30008000,那么解压地址应该是30008000。


(3)修改解压地址

解压地址定义在/arch/arm/mach-s5pv210/Makefile.boot文件中。

我们在该文件末尾添加以下内容:


(4)编译后重新下载,查看运行结果

因为只修改了一些链接参数,不需要重新配置编译,所以make很快完成。

运行结果如下图所示,自解压代码的解压信息已经显示出来,但内核还是没有运行。


(5)现象分析

1)我们在uboot中配置的地址空间是从3000 0000开始的,而不是2000 0000开始的。

2)但在/arch/arm/mach-s5pv210/include/mach/memory.h文件中,PHYS_OFFSET却定义为2000 0000。

3)因此需要将PHYS_OFFSET从20000000改到30000000,如下所示:


(6)重新编译,运行结果

内核打印出很多运行信息。

二、内核中的机器码

内核支持什么架构、支持哪款cpu,这是如何确定的?主要是通过机器码来确定的。内核中定义了一份机器码,uboot也会给内核传递一个机器码。在内核启动的汇编阶段,head.S文件对比uboot传给内核的机器码和内核定义的机器码,如果匹配则继续启动。

本节内容,主要讲内核如何定义机器码的。其实在内核源码——C语言阶段的start_kernel函数的第二点的第3点的第(4)点也有提及。


1、MACHINE_START宏

这个宏的内容如下:

(1)这个宏用来定义一个数据类型为struct machine_desc的变量,而且这个变量会被定义到一个特定段.arch.info.init,因此这个变量将来会被链接器链接到这个.arch.info.init段中。

(2)比如在/arch/arm/mach-s5pv210/mach-smdkv210.c中,有MACHINE_START(SMDKV210, "SMDKV210"),这就定义了一个数据类型为struct machine_desc的变量,这个变量名为__mach_desc_SMDKV210。这个变量是smdkv210这个开发板的抽象或者说表征。

static const struct machine_desc __mach_desc_SMDKV210	\__used							\__attribute__((__section__(".arch.info.init"))) = {	\.nr		= MACH_TYPE_SMDKV210,//机器码2456		\.name		= "SMDKV210",.phys_io	= S3C_PA_UART & 0xfff00000,.io_pg_offst	= (((u32)S3C_VA_UART) >> 18) & 0xfffc,.boot_params	= S5P_PA_SDRAM + 0x100,.init_irq	= s5pv210_init_irq,.map_io		= smdkv210_map_io,.init_machine	= smdkv210_machine_init,.timer		= &s5p_systimer,
};
  • 结构体成员.nr=MACH_TYPE_SMDKV210就是这个开发板的机器码。机器码值“MACH_TYPE_SMDKV210”在/include/generated/mach-types.h文件中定义,该文件在内核配置过程中自动生成。mach-types.h文件维护着该版本内核所支持的全部机器码。

  • 结构体成员“.name= "SMDKV210"”,表示该开发板的名称。

  • 结构体成员“.init_machine = smdkv210_machine_init”,定义了一个函数smdkv210_machine_init,该函数是一个硬件初始化函数,包含了内核中各种硬件的初始化信息。

(3)在/arch/arm/目录下有许多mach-xxx目录(不同目录代表不同CPU),这些mach-xxx目录中又有许多mach-xxx文件(同目录下的不同文件,表示相同CPU的不同开发板)。分析发现,每个mach-xxx.c文件中,都通过MACHINE_START宏,定义了一个表征某个开发板的struct machine_desc类型的变量。这些结构体变量都放到了.arch.info.init段,表示当前内核可以支持这些机器码对应的开发板。

(4)我们的目标开发板叫做X210,采用S5PV210这款CPU。在三星版本内核中到不到X210开发板对应的mach-x210.c文件,但为了减少移植工作量,在三星版本内核的/arch/arm/mach-s5pv210目录下寻找一个mach-xxx.c文件,使得该文件对应的开发板与X210开发板最为接近(使用相同的CPU),并以此文件为基础进行移植。

(5)九鼎的X210开发板是根据三星SMDKV210开发板进行设计的,因此要找SMDKV210开发板对应的文件,按理应该是mach-smdkv210.c文件。但分析/arch/arm/mach-s5pv210/Makefile文件后得知,内核配置生成的.config文件中定义CONFIG_MACH_SMDKV210后,实际绑定的是mach-smdkc110.c这个文件。因此实际上mach-smdkv210.c这个文件根本没用到。

(6)所以后续将以/arch/arm/mach-s5pv210/mach-smdkc110.c这个文件为基础进行移植。


2、硬件驱动的加载和初始化函数执行

/arch/arm/mach-s5pv210/mach-smdkc110.c文件中,有MACHINE_START(SMDKV210, "SMDKV210"),它展开后内容如下:

static const struct machine_desc __mach_desc_SMDKV210	\__used							\__attribute__((__section__(".arch.info.init"))) = {	\.nr		= MACH_TYPE_SMDKV210,//机器码2456		\.name		= "SMDKV210",.phys_io	= S3C_PA_UART & 0xfff00000,.io_pg_offst	= (((u32)S3C_VA_UART) >> 18) & 0xfffc,.boot_params	= S5P_PA_SDRAM + 0x100,.init_irq	= s5pv210_init_irq,.map_io		= smdkv210_map_io,.init_machine	= smdkv210_machine_init,//初始化各种硬件.timer		= &s5p_systimer,
};

注意“.init_machine    = smdkv210_machine_init,”,init_machine这个成员指向一个机器硬件初始化函数smdkv210_machine_init,这个函数绑定了内核启动过程中会初始化的各种硬件的信息,在此函数中添加的硬件才会被初始化,没有在此函数中添加的硬件不会被初始化。

关于smdkv210_machine_init的更多介绍,见下文第五点“网卡的问题与解决”,或者驱动栏目中的“Linux设备驱动模型”的内容。

三、电源管理IC的问题与解决

1、OOPS信息(内核死亡信息)

(1)内核启动后会有打印信息,这些信息隐藏了问题所在。认真分析这些信息,从中找到对的或者错误的一些信息片段,才能帮助我们找到问题,从而解决问题。

(2)内核启动过程的错误信息的特征

由PC和LR的值可以看出,程序执行到dev_driver_string或者max8698_pmic_probe(这两个是函数或者汇编中的标号)符号部分的时候出错了。我们从这两个符号出发去寻找、思考可能出错的地方,然后试图解决。


2、错误分析

(1)max8698_pmic_probe,看名字是max8698这个电源管理IC的驱动安装函数那部分出错了,应该是开发板系统中配置了支持这个电源管理IC,于是启动时去加载它的驱动,结果驱动在加载执行的过程中出错了OOPS。

(2)这个驱动加载时为什么会出错?结合X210开发板的硬件实际情况来分析,X210开发板上根本就没有max8698这个电源管理IC,既然硬件都没有,执行驱动肯定会出错。之前移植三星版本uboot时,在lowlevel_init.S中也调用了电源管理IC初始化函数(PMIC_init),结果报错,我们屏蔽掉该函数的调用后,uboot就可以成功运行下去。

(3)为什么uboot和内核,都默认调用这个电源管理IC的初始化代码?因为三星SMDKV210开发板中用了max8698这个电源管理IC,因此三星的uboot和kernel中都默认支持这个。但是X210中是没用的,因此uboot和内核中都需要去掉该代码模块。


3、错误解决

(1)如何解决错误

在uboot中是直接改源代码,屏蔽掉那个初始化函数解决的。但在内核中不能这么干,因为linux kernel是高度模块化、高度可配置的,内核中每一个模块都是被配置项条件编译的,因此要去掉某个模块的支持,只需要重新配置去掉选项即可,不用改源代码。所以我们的关键就是要找它对应的配置项。

(2)解决错误的过程

先make menuconfig,然后输入/,搜索"MAX8698"这几个关键字,然后看到这个配置项的路径,然后到路径下去按N键去掉这个模块的支持,保存,重新编译即可。

 (3)运行结果

重新编译后下载运行,发现此问题被解决,内核再次启动后直接运行到挂载rootfs才出错。


4、心得体会

(1)CONFIG_MFD_MAX8698这个配置宏,决定了drivers目录下的max8698对应的驱动程序源代码是否被编译,决定了kernel启动过程中是否会调用一些max8698的相关的代码。

(2)kernel是高度模块化和可配置化的,所以在内核中做任何事情(添加、更改、去掉一个模块)都必须按照内核设定的方案和流程。

四、iNand的问题与解决

1、OOPS信息

(1)内核试图挂载根文件系统时失败,失败原因是unknown-block(不能识别块设备)。

(2)分析backstrace,得知错误信息的来源于mount_block_root函数。

mount_block_root函数位于/init/do_mounts.c文件中,内容如下:


2、错误分析

(1)在kernel启动时,uboot给内核传递一个cmdline,其中“root=xxx”指定rootfs在哪个设备上,比如“root=/dev/mmcblk0p2”表示rootfs在mmc设备0的第2个分区(设备0,即在SD0通道上的设备,即iNand)。

(2)unknown-block(0,0),意思是没找到mmc设备0的第2分区。

(3)为什么没找到mmc设备0的第2分区?可能是kernel启动过程中加载mmc驱动时出现问题,驱动没有发现mmc设备0,于是问题定位在mmc相关的驱动方面。

(4)对比待移植的三星版本的内核启动信息、九鼎版本的内核启动信息,从中发现待移植的三星版本的内核启动时,没有找到MMC设备(内置的iNand和外置的SD卡都没有找到),这说明肯定是驱动的问题,因此要移植mmc驱动。


3、知识补充

(1)SD/iNand本身都是由一个一个的扇区组成的,裸机课程中X210的启动时,BL1在SD卡的1扇区开始往后存放,SD卡的0扇区是不用的。SD卡的0扇区是用来放置MBR的。

(2)MBR就是用来描述块设备的分区信息的,事先定义了一个通用的数据结构来描述块设备的分区,我们只要将分区信息写入MBR中即可对该设备完成分区。MBR默认就是在块设备的第0个扇区上存放的。

(3)uboot中的命令fdisk可以对iNand进行分区(执行“fdisk -c 0”即可),而且这个fdisk命令的内部已经用代码写死(将iNand分为多少个分区、每个分区的大小等等信息,已经用宏定义规定好,当然我们也可以修改这些宏定义)。内核通过读取iNand的MBR即可获取iNand的分区信息,不需要通过uboot给内核传参时传递这个分区信息。

(4)如果开发板使用的是nandFlash,分区表一般是在内核中用代码构建的。因此nand版本的内核移植时,一般都需要移植、更改nand分区表。


3、解决错误

暂略。因为涉及到驱动的内容,后续会补充。

五、网卡的问题与解决

1、OOPS信息

内核启动时,关于网卡部分的错误信息显示如下:


2、错误分析

(1)出错的原因是三星版本的内核还没有移植网卡驱动(有网卡驱动但没有移植)。

(2)在/arch/arm/mach-s5pv210/mach-smdkc110.c文件中,smdkc110_machine_init()函数是整个开发板所有硬件的初始化函数。此函数通过platform_add_devices()函数加载的硬件,将来启动时就会被初始化,没有被加载的硬件将来启动时就不管。所以网卡这个硬件相关的初始化内容,肯定位于smdkc110_machine_init()函数中。

(3)分析得知与网卡DM9000有关的是:smdkc110_devices结构体和smdkc110_dm9000_set()函数,需要分别做移植。

(4)smdkc110_dm9000_set()函数用来设置DM9000相关的SROM bank的寄存器,它相当于uboot中dm9000移植时的dm9000_pre_init函数,只是读写寄存器的函数名称不同而已。

(5)smdkc110_devices结构体中与网卡DM9000有关的设置,追踪如下。

在/arch/arm/mach-s5pv210/mach-smdkc110.c文件中有:

在/arch/arm/plat-s5p/devs.c中有:

在/arch/arm/plat-s5p/devs.c中有:


3、错误解决

(1)首先,在make menuconfig中添加DM9000支持。

通过在弹出的界面按/搜索“DM9000”,找到配置项所在路径,然后选择Y。其实这一步本来就是Y,因此这里不用设置。但是有些内核可能默认不是Y,因此要设置。

(2)接着,将九鼎移植好的/arch/arm/mach-s5pv210/mach-x210.c文件中的smdkc110_dm9000_set()函数,覆盖掉三星版本内核的/arch/arm/mach-s5pv210/mach-smdkc110.c文件中的smdkc110_dm9000_set()函数。

这主要是为了减少工作量,但修改的过程需要理解。

(3)接着,在arch/arm/plat-s5p/devs.c中修改DM9000相关的数据配置。

1)上图的S5P_PA_DM9000,定义在/arch/arm/mach-s5pv210/include/mach/map.h中,它表示DM9000的IO基地址,具体的值是与 DM9000接在哪个bank有关的。根据实际情况,将其改为0x88000300,如下所示。

2)因为九鼎的uboot源码/include/configs/x210_sd.h中有如下内容:

所以根据uboot的设置情况,下面的+2改为+4,如下所示。

3)根据实际情况,将两个IRQ_EINT9改成IRQ_EINT10。

(4)重新编译后下载运行,查看结果

网卡移植成功后,其启动信息显示如下:

六、内核启动汇编阶段的调试

1、调试的原因

(1)由之前的内容可知,内核启动汇编阶段主要体现在arch/arm/kernel/head.S文件。该文件中首先进行三个校验(CPU id的校验、机器码的校验、tag的校验),接着创建页表,然后构建C语言运行环境,最后跳转到start_kernel函数。基本上能运行到start_kernel,内核移植就不太会出问题了。

(2)但有时候移植的内核启动后,根本没有启动信息出来。针对这个问题,希望能有一种调试手段来确定问题所在。


2、调试的方法

(1)将led点亮和熄灭的汇编代码,复制粘贴到head.S文件中的合适位置,内核启动后根据led的表现来标明代码有无运行。

(2)典型的led函数如下所示。

/**************************************************************************
*函数作用:熄灭X210开发板的三颗LED灯
*函数说明
(1)寄存器r0,r1,r2在head.s文件中是用来给内核传参的,类似于全局变量。
因此不能用这些寄存器,我们可以使用那些没有被使用的寄存器,这里用r3,r4。
(2)X210开发板有四颗LED灯,启动时四颗都是半亮的。这个函数用来熄灭其中的三颗。
我们可以通过LED是否熄灭得知该函数之前的内核代码是否运行正常。
***************************************************************************/led:// 第一步:把0x11111111写入0xE0200240(GPJ0CON)位置ldr r3, =0x11111111	 ldr r4, =0xE0200240		str r3, [r4]  // 把r3中的数写入到r4中的数为地址的内存中去    // 第二步:把0xff写入0xE0200244(GPJ0DAT)位置ldr r3, =0xff // 开发板启动时led半亮ldr r4, =0xE0200244str r3, [r4]  // 把ff写入到GPJ0DAT寄存器中,引脚即输出高电平,三颗LED熄灭// 函数需要返回mov pc,lr          

3、动手测试

首先,在head.S文件中合适的地方(函数集中域处)定义这个led这个函数;

然后,在head.S文件中的某个地方调用这个led函数;

最后,重新编译内核并下载至开发板,通过查看LED的亮灭情况判断这段代码是否被运行。如果运行了,则说明调用这个led函数之前的内容都没有问题的;如果没有运行,则说明错误发生在调用这个led函数之前。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461636.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android中实时视频传输(摄像头实时视频传输)解决方案二

为什么80%的码农都做不了架构师?>>> 1、使用FFMpeg进行视频采集,使用Live555进行RTP传输,使用VideoView进行播放。 csdn提到:重载FrameSource,写一个服务类,可以从FrameSource的派生类读取帧数…

定时器——S5PV210定时器的理论与操作

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。 一、定时器简介 1、定时器的含义 定时器作为SoC的外设,主要用来实现定时执行代码的功能,它相对SoC而言,就像闹钟相对于人的意义一样。定时器内部的计数器每隔一个…

uboot的移植——DM9000移植的理论基础

以下内容源于朱有鹏嵌入式课程的学习,如有侵权请告知删除。 一、网卡相关的基本知识 1、DM9000网卡芯片和SoC的连接 如上图所示,DM9000网卡芯片是通过SROM总线接口,或者说SROM控制器接入SoC的。下面分别介绍SROM控制器、DM9000网卡芯片的相关…

javascript深入浅出

第一章 数据类型 1,六种数据类型:原始类型(number,string,boolean,null,undefined) object对象(Function Array Date) 2,隐式转换:Na…

网络通信基础常识

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。 1、网络通信属于进程间通信 进程间通信的一种方法是使用套接字socket,网络通信其实就是位于网络中不同主机上面的2个进程之间的通信。 2、网络通信的层次 网络通信的层次,即…

Android NDK工程创建与编译运行

首发地址:http://www.eoeandroid.com/thread-201993-1-1.html一、 工程创建1. 创建一个Android工程配置好开发环境并加载好对应版本sdk后,选择菜单File->New-> Project创建工程,工程类型选择Android Project,如下图所示&…

[Quatsch]Quantum Or Optics

小生预言,鉴于目前CG产业中学术界与生产界之间的差异,未来必将有一门独立的学科,专门用于研究自然界的材质特性与表现,从此艺术界与学术界将呈现完美之统一,应该先在MIT或者Cornell出现。本人姑且将这门学科命名为“应…

C#事件 的讲解

一、事件的本质事件是软件系统里的两个子系统之间,或者两个模块之间,或者两个对象之间发送消息,并处理消息的过程。在面向对象的世界里,就可以统一认为是两个对象之间的行为。两个对象之间发送的这种消息,对发送方来讲…

phpMyAdmin密码设置

2019独角兽企业重金招聘Python工程师标准>>> 在使用phpMyAdmin操作数据库时,首先需要设置mysql的root用户的密码。具体操作参考前一篇博文。设置完mysql的root密码之后,需要对phpMyAdmin的登录进行一些配置。 未经配置的phpMyAdmin很不安全&a…

抽取网络信息进行数据挖掘 建立语料库

最近的实习项目需要做一个大数据库(语料库),采集博客、微博、问答库的信息。将数据库的内容进行训练,最后应该是做成一个类似中文siri的模型吧。 第一步新闻抓取器已经稳定运行了,基本原理用的是爬虫去爬新闻门户网站的…

四旋翼无人机调研结果

P.S:作者曾在大学某次小班课上看到该视频,深深被震撼了,因此对无人机念念不忘。 恰逢某课程要求讲解自己喜欢的领域的内容,因此对四旋翼无人机进行了调研。

选购四轴飞行器的部件

内容截于:http://www.loveuav.com/article-191-1.html

重复编辑命令行

为什么80%的码农都做不了架构师?>>> 要想重复前面已经输入的命令,请按向上方向键。每按这个键一次,shell都会显示前一个命令行。要想重新执行所显示的命令行,请按回车键。按向下方向键,则可以沿着相反的方…

hadoop2.2.0 分布式存储hdfs完全分布式搭建及功能测试记录(一)----架构及原理介绍...

0.文档说明:本文是围绕hadoop2.2的分布式文件系统hdfs进行分布式存储功能测试,形成的hdfs分布式存储功能测试报告,其中主要包括三大部分内容:第一部分介绍了hdfs的基本原理;第二部分介绍了hadoop2.2的完全分布式集群安…

宏定义函数container_of的解释

从kernel里面抠出的一些与宏container_of有关的代码,如下: 1、此宏作用是从结构体的某元素(member)出发,得到结构体的首地址; 2、container_of的参数解释 (1)type:指的是…

【ASP.NET Web API教程】2.3 与实体框架一起使用Web API

2.3 Using Web API with Entity Framework 2.3 与实体框架一起使用Web API 本小节是ASP.NET Web API第2章的第3小节,原文共分为7个部分,分成了7篇文章,故这里也分为7个帖子贴出,以下是本小节的第1部分 — 译者注。 Part 1: Overvi…

ITTC数据挖掘平台介绍(综述)——平台简介

数据挖掘方兴未艾,大量新事物层出不穷。本系列将介绍我们自主设计的数据挖掘软件平台。与大家共同分享对知识,微博,人际等复杂网络的分析,以及对自然语言处理的见解。 一、我们需要怎样的数据挖掘系统 一直以来,以高校…

Linux—程序包安装与管理

1、软件包是对于一种软件所进行打包的方式。在不同的操作系统中,软件包的类型有很大的区别。对于Linux系统中,软件包主要以两种形式出现:二进制包以及源代码包。二进制包:1)传统的red hat linux二进制包2)d…

Master-Detail(主表明细),确认可以出货的SQL指令 -- Not Exists

这是我文章的备份,原文请看: http://www.dotblogs.com.tw/mis2000lab/archive/2011/08/18/master_detail_finish_and_shipping.aspx [补充]下集,第十四章。Master-Detail(主表明细),确认可以出货的SQL指令…

开发板——X210开发板的SD卡启动方式

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。 前言与总结 这里说的SD卡启动方式,指的是uboot在SD卡中或者在inand里,且启动介质拨码开关选择SD卡启动方式(对于X210,是拨到远离电源键的一侧&#xf…