Linux 内核移植

Last edited
Last updated July 30, 2023
Pages
Tags
实验

1. 移植准备工作

1.1 安装必要的依赖工具

$ sudo apt-get install bison flex libelf-dev libssl-dev u-boot-tools libncurses-dev

1.2 下载源码

  • 数据包下载:STM32MP1Dev 页面中的 Yocto_SDK
    • 本文使用 3.1 版本,对应 Linux 5.10.61 版本内核
  • 文件名为 en.SOURCES-stm32mp1-openstlinux-5.10-dunfell-mp1-21-11-17_tar_v3.1.0.xz
  • 解压后拷贝出 sources/arm-ost1-linux-gnueabi/linux-stm32mp-5.10.61-stm32mp-r2-r0
  • 该目录下有:
    • .patch:补丁文件
    • .config:补丁配置列表
    • linux-5.10.61.tar.xz:Linux 系统源码
    • README.HOW_TO.txt
    • series:补丁文件列表
  • 解压 linux-5.10.61主版本号.次版本号.修订版本号
  • 进入解压后的源码目录,打上补丁(参考 README
for p in `ls -1 ../*.patch`; do patch -p1 < $p; done
  • linux-5.10.61.tar.xz 是单纯的 Linux 源码,打补丁的作用是添加 ST 的各个设备的设备树文件

1.2 配置交叉编译工具链

  • 打开 Makefile 编辑如下行:
370 ARCH ?= arm 371 CROSS_COMPILE:=arm-linux-gnueabihf-

1.3 构建内核源码补丁文件的默认配置

make ARCH=arm multi_v7_defconfig fragment*.config # 成功现象 # (中间省略) # # configuration written to .config #

1.4 拷贝设备树文件

  • FSMP1A 是从 DK1 公版基础上修改的,所以可以从 DK1 的设备树文件修改
cd arch/arm/boot/dts cp stm32mp157a-dk1.dts stm32mp157a-fsmp1a.dts cp stm32mp15xx-dkx.dtsi stm32mp15xx-fsmp1x.dtsi
  • 编辑 stm32mp157a-fsmp1a.dts,修改依赖的头文件:
13 #include "stm32mp15xx-fsmp1x.dtsi" 14 15 / { 16 model = "HQYJ STM32MP157A-FSMP1A Discovery Board"; 17 compatible = "hqyj,stm32mp157a-fsmp1a", "hqyj,stm32mp157";
  • 编辑 arm/boot/dts/Makefile,添加到编译的列表
1097 stm32mp157a-dk1.dtb \\ 1098 stm32mp157a-fsmp1a.dtb \\ ------> 这一行为需要添加内容

2. 首次Linux内核的编译运行

2.1 首次编译

  • 可以参考 README 的第六章节
time make -j4 ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000000
  • time:对编译进行计时
  • ARCH=arm:指定 arm 架构
  • uImage:编译生成的镜像文件
  • vmlinux:指定 elf 镜像文件
  • dtbs:编译设备树文件
  • LOADADDR=0xC20000000:指定的加载地址
  • 编译成功后显示:
OBJCOPY arch/arm/boot/zImage Kernel: arch/arm/boot/zImage is ready UIMAGE arch/arm/boot/uImage Image Name: Linux-5.10.61 Created: Tue May 9 17:10:31 2023 Image Type: ARM Linux Kernel Image (uncompressed) Data Size: 7172240 Bytes = 7004.14 KiB = 6.84 MiB Load Address: c2000000 Entry Point: c2000000 Kernel: arch/arm/boot/uImage is ready

2.2 安装镜像到开发板

  • 通过 tftp 服务器下载镜像到开发板的方法安装
    • 需要开发板和主机在同一局域网
    • 需要配置好 tftp 服务器:
      TFTP 环境配置
  • 步骤一:打开串口工具连接到开发板 u-boot,查看并设置 bootcmdbootargs (⚠️一定要注意对应自己的路径)
FSMP1A> pri bootcmd bootcmd=tftp 0xc2000000 uImage;tftp 0xc4000000 stm32mp157a-fsmp1a.dtb;bootm 0xc2000000 - 0xc4000000 FSMP1A> pri bootargs bootargs=root=/dev/nfs nfsroot=192.168.1.250:/home/linux/nfs/rootfs,tcp,v4 rw console=ttySTM0,115200 init=/linuxrc ip=192.168.1.100
  • bootcmd 即开机自动运行的脚本,也可以在 u-boot 交互中手动输入这些代码
  • 指令解析:
    • tftp 下载地址 镜像tftp 下载地址 设备树文件bootm 启动范围
    • 0x20000000 需要对应首次编译指令中输入的地址,0x40000000 设置一个距离一个距离 0x20000000足够远的地址,没有特定
    • nfsroot :通过 nfs 挂载 rootfs 根文件系统
    • 192.168.1.250:主机地址,nfs 服务器地址
    • 192.168.1.100:开发板设置的局域网地址
    • /home/linux/nfs/rootfs :主机中 rootfs 的目录
  • 在开发板 u-boot 确认网络相关配置:
FSMP1A> pri serverip serverip=192.168.1.250 FSMP1A> pri netmask netmask=255.255.255.0 FSMP1A> pri gatewayip gatewayip=192.168.1.1 FSMP1A> pri ipaddr ipaddr=192.168.1.100 FSMP1A> ping 192.168.1.250 ethernet@5800a000 Waiting for PHY auto negotiation to complete... done Using ethernet@5800a000 device host 192.168.1.250 is alive
  • 确认服务器地址(即主机的地址),确认子网掩码,确认网关 IP
  • 通过 ping 测试,显示alive即为网络连接 OK
  • 接下来,拷贝编好的镜像文件和设备树文件到 tftp 目录中
cp arch/arm/boot/uImage ~/tftpboot/ cp arch/arm/boot/dts/stm32mp157a-fsmp1a.dtb ~/tftpboot/
  • 复位开发板,等待 u-boot 倒计时后自动运行 bootcmd 下载镜像并运行:
[ 5.637332] IP-Config: Fail to open eth0 [ 5.641332] IP-Config: No netwrok devices avaliable
  • 会跳出熟悉的 Linux 启动日志,但最终会停止在网络驱动错误中(这是正常的,因为还没有对设备树文件修改)

3. 解决内核错误

3.1 修改设备树文件

  • 0001-linux-5.10.61-maxio0621.patch 拷贝到源码目录中,这是 v3.1 版本开发板的网卡驱动
    • 执行patch -Np1 < 0001-linux-5.10.61-maxio0621.patch打上补丁
    • 显示 patching file scripts/dtc/include-prefixes/arm/stm32mp157a-fsmp1a.dts 即可(可忽略其他错误)
    • 查看 stm32mp157xx-fsmp1x.dtsi,是否包含:
 
&ethernet0 { status = "okay"; pinctrl-0 = <&ethernet0_rgmii_pins_a>; pinctrl-1 = <&ethernet0_rgmii_sleep_pins_a>; pinctrl-names = "default", "sleep"; phy-mode = "rgmii-id"; max-speed = <1000>; phy-handle = <&phy0>; mdio0 { #address-cells = <1>; #size-cells = <0>; compatible = "snps,dwmac-mdio"; phy0: ethernet-phy@0 { reg = <0>; }; }; }
 
  • 通过 KConfig 来开启配置:
$ make menuconfig Device Drivers ---> [*] Network device support ---> [*] Ethernet driver support ---> <*> STMicroelectronics Multi-Gigabit Ethernet driver <*> STMMAC Platform bus support <*> Support for snps,dwc-qos-ethernet.txt DT binding. <*> Generic driver for DWMAC <*> STM32 DWMAC support
 
  • 添加 MAE0621A 驱动( v3.1 开发板的驱动 ):
make menuconfig Device Drivers ---> [*] Network device support ---> -*- PHY Device support and infrastructure ---> < > Quality Semiconductor PHYs <*> Realtek PHYs <*> MAXIO PHYS -------> #需要按下键盘的空格键两下,一定选择为*!!!!!

3.2 重新编译并拷贝镜像和设备树文件

$ time make -j4 ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000000 $ cp arch/arm/boot/uImage ~/tftpboot/ $ cp arch/arm/boot/dts/stm32mp157a-fsmp1a.dtb ~/tftpboot/
  • 此时,再次复位开发板,可以进入 Linux 的 shell,但是还有一个错误:
    • 如果显示的依然是 IP 相关的错误,可以尝试 make clean 后完全重头编译
/etc/init.d/rcS: line 6: can't create /proc/sys/kernel/hotplug: nonexistent directory
  • 这个需要开启内核的热加载功能:
$ make menuconfig Device Drivers ---> Generic Driver Options ---> [*] Support for uevent helper (/sbin/hotplug) path to uevent helper
  • 再次通过上面的命令编译拷贝文件重启设备
  • 此时可以无错误进入 Linux 的 Shell 交互界面中

4. make menuconfig 的执行过程

4.1 Makefile分析

  • 打开源码目录下的 Makefile,查找 menuconfig 选项
  • 没有找到 menuconfig 选项,考虑可能使用通配符,查找 %
  • 在 vim 中输入 /^% 查找 % 开头的行,找到 %config (602行附近):
%config: outputmakefile scripts_basic FORCE $(Q)$(MAKE) $(build)=scripts/kconfig $@
  • 通过 echo 去查看输出的内容
602 %config: outputmakefile scripts_basic FORCE 603 @echo $(Q) 604 @echo $(MAKE) 605 @echo $(build) 606 @echo $@ 607 $(MAKE) $(build)=scripts/kconfig $@
  • 由于 $(Q) 只是 @ ,对输出内容进行回显,所以在在构建命令中删除方便理解构建指令
  • 输出如下的结果:
@ make -f ./scripts/Makefile.build obj menuconfig make -f ./scripts/Makefile.build obj=scripts/kconfig menuconfig
  • 上面最后一条是完整的构建指令,其中 f 是指定另一个 Makefile 运行
  • 所以需要再次进入 ./scripts/Makefile.build 这个 Makefile 文件进行分析

4.2 scirpts/Makefile.build分析

  • 通过搜索命令行的参数 obj,可以发现它在第 6 行被赋值给脚本中的变量 src
  • 同时可以看到一些 obj 对应目录下的文件的编译,也就是说 kconfig 会在第一次运行的时候进行编译
6 src := $(obj) --------> 解释:src := scripts/kconfig 42 kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) 解释: kbuild-dir := $(src) = scripts/kconfig 43 kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) 解释: kbuild-file := scripts/kconfig/Makefile 44 include $(kbuild-file) 解释:将scripts/kconfig目录下的Makefile文件,替换到当前文件的这个位置
  • 因为找不到其他相关的构建指令,再从 include 相关行查看
  • include 会将指定位置下的 Makefile 加载到当前 Makefile 运行,所以需要再次追踪 scripts/kconfig/Makefile

4.3 script/kconfig/Makefile分析

28 menuconfig: $(obj)/mconf 29 $< $(silent) $(Kconfig) 解释: scripts/kconfig/mconf Kconfig mconf是一个可执行文件,kconfig作为参数传递给mconf
  • 这个 Makefilemenuconfig 指令
  • $< 代表构建指令中,依赖列表第一个项目的名称,silentsKconfig 变量是 Kconfig 指令
  • 所以最后是 script/kconfig/mconf Kconfig
  • mconf 是一个二进制可执行文件,Kconfig 为源码目录下的当前配置文件

5. 配置文件

5.1 三个配置文件的关系

  • Makefile:管理内核源码配置和编译
  • Kconfig:存放 kconfig 图形化配置信息
  • .config:生成默认的配置文件,给 Makefile 使用

5.2 Kconfig 语法

  1. 概念:
      • 主菜单:可包含子菜单和菜单选项
      • 子菜单:可包含子菜单和菜单选项
      • 子子菜单:可包含菜单选项
      • 菜单选项:选择需要执行的内容
  1. 打开 Kconfig文件:
      • 第一行为 mainemnu 开头项,这是菜单的名称
        • 例如,mainmenu "Linux/$(ARCH) $(KERNELVERSION) Kernel Configuration"
      • 其余行为 source 开头项,这是子菜单
        • 例如,source "drivers/Kconfig"
  1. 打开 drivers/Kconfig
      • menu 开头行,这是菜单名称
      • source 开头行,这是子菜单
        • 例如 source "drivers/char/Kconfig"
      • endmenu 行,表示菜单结束
  1. 打开 drivers/char/Kconfig
      • config 开头行,这是菜单选项
      • 菜单选项的语法:
        • <> : tristate 三态
          • <*> 表示对应内容编译到镜像文件中
          • < > 表示对应内容不编译到镜像文件中
          • <M> 表示对应内容通过模块化编译
        • [] 布尔值
          • [*] 表示编译到镜像文件中
          • [ ] 表示不编译到镜像文件中
      • depends on 表示依赖某一个菜单选项
      • default:默认的选择方式
      • help:选项帮助信息

6. 编译外设驱动

  • 以编译开发板的 LED 灯为例
  • 拷贝 fsmp1a_led.cfsmp1a_led.hdrivers/char 目录下
  • 编辑 driver/char/Kconfig 文件,添加如下配置:
config LED_DRIVER bool "led driver" default y help this is DC23031 led driver!!!
  • driver/char/Makefile 文件添加:
50 obj-$(CONFIG_LED_DRIVER) += fsmp157a_led.o
  • 进入图形化配置界面进行配置
$ make menuconfig Device Drivers ---> Character devices ---> [*] led driver (NEW)
  • 进行编译和拷贝
  • 拷贝刚才 LED 灯驱动文件包中的测试代码 fsmp1a_led_test.c 和头文件 fsmp1a_led.hnfs/rootfs 即挂载的目录下
  • 通过主机的交叉编译工具进行编译:gcc-linux-gnueabihf-gcc fsmp1a_led.c
  • 在开发板的 Linux Shell 中运行编译后的程序 ./a.out
  • 此时 LED 灯依据测试文件代码进行闪烁
  • 框图:
应用层:(0~3G) a.out -------> 实现的逻辑 执行应用层程序,函数入口main函数 ——————————————————————————————————————————————————————————————— 内核层:(3G~4G) fsmp157a_led.o -------> 实现的机制 执行驱动相关程序,函数入口init 函数出口exit ——————————————————————————————————————————————————————————————— 硬件层 LED 蜂鸣器 马达

7. 采用模块化的方法编译设备驱动

  • 修改 driver/char/Kconfig
8 config LED_DRIVER 9 tristate "led driver" 10 default y 11 help 12 this is DC23031 led driver!!!
  • 进行图形化配置
vice Drivers ---> Character devices ---> <M> led driver ---> !!!更改为M选项 !!!
  • 编译和拷贝
  • 此时还需要拷贝 dirver/char/fsmp157a_led.korootfs
  • 通过开发板 Linux 运行 insmod fsmp157a_led.ko 加载驱动到内核中
  • 再次运行 ./a.out,程序再次工作
  • 通过 rmmod fsmp157a_led 移除驱动
  • 再次运行 ./a.out,程序错误退出

8. 镜像编译过程分析

8.1 uImage镜像文件的构建

  • 通过 README 参考,镜像文件的编译指令为 make uImage LOADADDR=0xC20000000
  • 查找 Makefile ,没有找到 uImage 关键字
  • 查看 include 开头行,查看是否是其他 Makefile 引入
  • 但是 includeMakefile 比较多,先用 grep 看看哪些 Makefile 构建了 uImages
    • grep -r 'uImage' ./*/**/Makefile
    • 结果基本上都是 ./arch 开头
  • 这样可以确定,应该是 596 行的 arch/$(SRCARCH)/Makefile 引入的
  • 所以对应的是 arch/arm/Makefile 中构建 uImage 相关的指令
  • 进入 arch/arm/Makefile 文件,查看 uImage 相关行
315 BOOT_TARGETS = zImage Image xipImage bootpImage uImage 323 $(BOOT_TARGETS): vmlinux 324 @echo $(Q) 325 @echo $(MAKE) 326 @echo $(build) 327 @echo $(boot) 328 @echo $(MACHINE) 329 @echo $(boot) 330 @echo $@ 331 $(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ 332 @$(kecho) ' Kernel: $(boot)/$@ is ready'
  • 得到打印信息
@ make -f ./scripts/Makefile.build obj arch/arm/boot arch/arm/boot uImage make -f ./scripts/Makefile.build obj=arch/arm/boot MACHINE= arch/arm/boot/uImage
  • 我们在上面#scirpts/Makefile.build分析分析过 script/Makefile.build 它相当于调用 obj 对应的 Makefile 并在 obj 第一次运行时构建对应的一些文件
  • 所以进入 arch/arm/boot/Makefile 文件,查看 uImage 相关构建指令
89 $(obj)/uImage: $(obj)/zImage FORCE 90 @$(check_for_multiple_loadaddr) ——————————>检查加载地址 91 $(call if_changed,uimage) ——————————>call是调用命令,调用if_changed命令
  • 第 90 行为一个函数,会检查 LOADADDR 参数,如果缺少会报错终止构建
  • 而第 91 行的函数则找不到出处,反过来想,因为这个 Makefile 是被 script/Makefile.buildinclude 的,所以应该实在 script/Makefile.build 中定义的
  • 再次进入 script/Makefile.build 查看 if_changed 相关行:
218 if_changed = $(if $(newer-prereqs)$(cmd-check), \\ 219 $(cmd); \\ 183 cmd = @set -e; $(echo-cmd) $($(quiet)redirect) $(cmd_$(1)) 解释: @set -e:编译时遇到任何错误就退出 $(cmd_$(1)) :调用cmd_uimage
  • cmd_uimage 是哪个 include 中引入的,首先可以通过命名检查 Makefile.lib 文件,在其中搜索 cmd_uimage
398 cmd_uimage = $(BASH) $(MKIMAGE) -A $(UIMAGE_ARCH) -O linux \\ 399 -C $(UIMAGE_COMPRESSION) $(UIMAGE_OPTS-y) \\ 400 -T $(UIMAGE_TYPE) \\ 401 -a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \\ 402 -n $(UIMAGE_NAME) -d $< $@ 解释: $(BASH) :bash MKIMAGE := 内核源码目录下/scripts/mkuboot.sh 通过分析mkuboot.sh脚本文件可知,执行mkimage命令 mkimage -A arm -O linux -C gzip -T kernel -a 0xc2000000 -e 0xc2000000 -n uImage -d zImage
  • 总结 uImagezImage 之间的关系:
    • uImage是通过zImage得到,将zImage通过 mkimage这个工具添加64字节头部信息
    • 每次编译成功之后,都会打印添加信息内容
Image Name: Linux-5.10.61 Created: Tue May 9 17:10:31 2023 Image Type: ARM Linux Kernel Image (uncompressed) Data Size: 7172240 Bytes = 7004.14 KiB = 6.84 MiB Load Address: c2000000 Entry Point: c2000000

8.2 zImage镜像文件的构建

1.进入linux内核源码目录下,arch/arm/boot目录下,打开Makefile文件,搜索zImage 66 $(obj)/zImage: $(obj)/compressed/vmlinux FORCE 67 $(call if_changed,objcopy) ——————————————>执行cmd_objcopy 2.进入linux内核源码目录下,scripts目录下,打开Makefile.lib文件,搜索cmd_objcopy 267 cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@ arm-linux-gnueabihf-objcopy arch/arm/boot/compressed/vmlinux arch/arm/boot/zImage 3.zImage镜像文件和arch/arm/boot/compressed/vmlinux之间关系 arch/arm/boot/compressed/vmlinux通过objcopy命令格式化转换为zImage

8.3 $(obj)/compressed/vmlinux镜像文件的构建

1.进入linux内核源码目录下,arch/arm/boot目录下,打开Makefile文件,搜索vmlinux 63 $(obj)/compressed/vmlinux: $(obj)/Image FORCE 64 $(Q)$(MAKE) $(build)=$(obj)/compressed $@ 命令:make -f ./scripts/Makefile.build obj=arch/arm/boot/compressed arch/arm/boot/compressed/vmlinux 2.进入linux内核源码目录下,arch/arm/boot/compressed目录下,打开Makefile文件,搜索vmlinux 178 $(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o \\ 179 $(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) \\ 180 $(bswapsdi2) $(efi-obj-y) FORCE 182 $(call if_changed,ld) 将所有的.o文件通过ld命令,根据vmlinux.lds链接脚本文件,链接生成vmlinux 185 $(obj)/piggy_data: $(obj)/../Image FORCE 186 @echo "!!!!!!!!!$(compress-y)!!!!!!!!" 187 $(call if_changed,gzip) ——————————————>执行cmd_gzip 188 $(obj)/piggy.o: $(obj)/piggy_data 3.进入linux内核源码目录下,scripts目录下,打开Makefile.lib文件,搜索cmd_gzip 273 cmd_gzip = cat $(real-prereqs) | $(KGZIP) -n -f -9 > $@ 4.arch/arm/boot/compressed/vmlinux镜像文件和Image之间关系 将Image镜像gzip压缩方式得到arch/arm/boot/compressed/vmlinux镜像文件

8.4 Image镜像文件的构建

1.进入linux内核源码目录下,arch/arm/boot目录下,打开Makefile文件,搜索Image 60 $(obj)/Image: vmlinux FORCE 61 $(call if_changed,objcopy) ——————————————>执行cmd_objcopy 2.Image和vmlinux之间关系 vmlinux通过objcopy命令格式化转换为Image

8.5 镜像文件的构建过程

vmlinux ——>objcopy——>Image ——>gzip——>arch/arm/boot/compressed/vmlinux ——>objcopy——>zImage ——>mkimage——>uImage

8.6 vmlinux镜像文件的构建

1.进入linux内核源码目录下,打开Makefile文件,搜索:vmlinux 1182 vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE 1183 +$(call if_changed,link-vmlinux) ——————————>调用cmd_link-vmlinux 1178 cmd_link-vmlinux = \\ 1179 $(CONFIG_SHELL) $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)"; \\ 1180 $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true) 通过以上分析可知:运行脚本文件 scripts/link-vmlinux.sh 2.进入linux内核源码目录下,打开scripts/link-vmlinux.sh 文件, 12 # vmlinux 13 # ^ 14 # | 15 # +--< $(KBUILD_VMLINUX_OBJS) 16 # | +--< init/built-in.a drivers/built-in.a mm/built-in.a + more 17 # | 18 # +--< $(KBUILD_VMLINUX_LIBS) 19 # | +--< lib/lib.a + more 20 # | 21 # +-< ${kallsymso} (see description in KALLSYMS section) 3.进入linux内核源码目录下,打开scripts/link-vmlinux.sh 文件, 48 modpost_link() 49 { 50 local objects 51 52 objects="--whole-archive \\ 53 ${KBUILD_VMLINUX_OBJS} \\ 54 --no-whole-archive \\ 55 --start-group \\ 56 ${KBUILD_VMLINUX_LIBS} \\ 57 --end-group" 58 59 ${LD} ${KBUILD_LDFLAGS} -r -o ${1} ${objects} 60 } 4.进入linux内核源码目录下,打开Makefile文件, 1136 KBUILD_VMLINUX_OBJS := $(head-y) $(patsubst %/,%/built-in.a, $(core-y)) 1137 KBUILD_VMLINUX_OBJS += $(addsuffix built-in.a, $(filter %/, $(libs-y))) 1138 ifdef CONFIG_MODULES 1139 KBUILD_VMLINUX_OBJS += $(patsubst %/, %/lib.a, $(filter %/, $(libs-y))) 1140 KBUILD_VMLINUX_LIBS := $(filter-out %/, $(libs-y)) 1141 else 1142 KBUILD_VMLINUX_LIBS := $(patsubst %/,%/lib.a, $(libs-y)) 1143 endif 1144 KBUILD_VMLINUX_OBJS += $(patsubst %/,%/built-in.a, $(drivers-y)) 654 core-y := init/ usr/ 655 drivers-y := drivers/ sound/ 656 drivers-$(CONFIG_SAMPLES) += samples/ 657 drivers-y += net/ virt/ 658 libs-y := lib/