报错日志:
[root@edgenode1 ~]# fdisk -l
Disk /dev/ram0: 4 MiB, 4194304 bytes, 8192 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disk /dev/mmcblk0: 116.48 GiB, 125074145280 bytes, 244285440 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 878D3D1E-1622-41C8-C177-AD673846C697
Device Start End Sectors Size Type
/dev/mmcblk0p1 16384 32767 16384 8M unknown
/dev/mmcblk0p2 32768 40959 8192 4M unknown
/dev/mmcblk0p3 40960 565247 524288 256M unknown
/dev/mmcblk0p4 565248 827391 262144 128M unknown
/dev/mmcblk0p5 827392 892927 65536 32M unknown
/dev/mmcblk0p6 892928 244285406 243392479 116.1G unknown
[ 77.879852] Unable to handle kernel paging request at virtual address ffff7fc10593f1be
[ 77.887786] Mem abort info:
[ 77.890602] ESR = 0x96000004
[ 77.893674] EC = 0x25: DABT (current EL), IL = 32 bits
[ 77.898994] SET = 0, FnV = 0
[ 77.902065] EA = 0, S1PTW = 0
[ 77.905215] Data abort info:
[ 77.908089] ISV = 0, ISS = 0x00000004
[ 77.911935] CM = 0, WnR = 0
[ 77.914904] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001c01000
[ 77.921608] [ffff7fc10593f1be] pgd=0000000000000000, p4d=0000000000000000
[ 77.928398] Internal error: Oops: 96000004 [#1] SMP
[ 77.933267] Modules linked in: sd_mod uas usb_storage scsi_mod xt_comment rtc_aip8563(O)
[ 77.941361] CPU: 6 PID: 1243 Comm: fdisk Tainted: G O 5.10.0 #63
[ 77.948653] Hardware name: Rockchip RK3588 EVB4 LP4 V10 Board (DT)
[ 77.954829] pstate: 00400009 (nzcv daif +PAN -UAO -TCO BTYPE=--)
[ 77.960830] pc : __memcpy+0x2c/0x180
[ 77.964404] lr : kmemdup+0x60/0x90
[ 77.967802] sp : ffff80001362bc80
[ 77.971108] x29: ffff80001362bc80 x28: ffff00011376ac40
[ 77.976411] x27: 0000000000000000 x26: 0000000000000000
[ 77.981714] x25: 0000000000000000 x24: 0000000000000000
[ 77.987016] x23: 00000000480a101d x22: ffff000108d35000
[ 77.992319] x21: ffff7fc10593f1be x20: 0000000000000042
[ 77.997621] x19: ffff00010ab49280 x18: 0000000000000000
[ 78.002924] x17: 0000000000000000 x16: 0000000000000000
[ 78.008225] x15: 0000000000000000 x14: 0000000000000000
[ 78.013526] x13: 0000000000000000 x12: 0000000000000056
[ 78.018828] x11: 0000000000000024 x10: 0000000000000a60
[ 78.024140] x9 : ffff80001022531c x8 : ffff00011376b700
[ 78.029442] x7 : 0000000000000004 x6 : ffff00010ab49280
[ 78.034743] x5 : 0000000000000000 x4 : 0000000000000002
[ 78.040054] x3 : 0000000000000080 x2 : 0000000000000040
[ 78.045356] x1 : ffff7fc10593f1be x0 : ffff00010ab49280
[ 78.050659] Call trace:
[ 78.053102] __memcpy+0x2c/0x180
[ 78.056336] scsi_bios_ptable+0x84/0xe4 [scsi_mod]
[ 78.061128] scsi_partsize+0x24/0x120 [scsi_mod]
[ 78.065748] scsicam_bios_param+0x24/0x114 [scsi_mod]
[ 78.070800] sd_getgeo+0xc8/0xe4 [sd_mod]
[ 78.074805] blkdev_ioctl+0x160/0x2c0
[ 78.078459] block_ioctl+0x44/0x54
[ 78.081860] vfs_ioctl+0x30/0x50
[ 78.085082] __arm64_sys_ioctl+0x68/0x9c
[ 78.089003] el0_svc_common.constprop.0+0x13c/0x1f0
[ 78.093870] do_el0_svc+0x84/0xa4
[ 78.097187] el0_svc+0x20/0x30
[ 78.100241] el0_sync_handler+0xcc/0x154
[ 78.104160] el0_sync+0x1a0/0x1c0
[ 78.107466]
[ 78.107466] PC: 0xffff8000106f266c:
[ 78.112418] 266c cb040060 d65f03c0 d343fc4c b400048c f240081f 540001a0 8b0a0000 8b0a0021
[ 78.120595] 268c cb0a0042 d343fc4c b40003ac f8408403 f8408424 f100058c ca040066 da9f10c7
linux内核、用户空间的内存划分:
如下图:32位系统内核空间划分0~3G为用户空间,3~4G为内核空间。详细请参考《Linux用户空间与内核空间》
注意:内核地址空间的范围是 0xC0000000 ~ 0xFFFFFFFF
而对于64位系统,内核空间划分如下:
ARM64架构处理器采用48位物理寻址机制,最大可以寻找到256TB的物理地址空间。对于目前的应用来说已经足够了,不需要扩展到64位的物理地址寻址。虚拟地址也同样最大支持48位支持,所以在处理器的架构设计上,把虚拟地址空间划分为两个空间,每个空间最大支持256TB。Linux内核在大多数体系结构中都把两个地址空间划分为用户空间和内核空间。
-
用户空间:0x0000_0000_0000_0000到0x0000_ffff_ffff_ffff
-
内核空间:0xffff_0000_0000_0000到0xffff_ffff_ffff_ffff
64位的Linux内核已经没有高端内存的概念了,因为48位的寻址空间已经足够大了
在QEMU实验平台上,ARM64架构的LInux内核的内存分布图如下:
如图所示,ARM64架构处理器的Linux内核内存布局图。ARM64架构处理器的Linux内核内存布局如下:
(1)用户空间:0x0000_0000_0000_0000到0x0000_ffff_ffff_ffff,一共有256TB。
(2)非规范区域
(3)内核空间:0xffff_0000_0000_0000到0xffff_ffff_ffff_ffff。一共有256TB。
内核空间又做了如下细分:
- vmalloc区域:0xffff_0000_0000_0000到0xffff_7bff_bfff_0000,大小为126974GB。
- vmemmap区域:0xffff_7bff_c000_0000到0xffff_7fff_c000_0000,大小为4096GB。
- PCI I/O区域:0xffff_7fff_ae00_0000到0xffff_7fff_be00_0000,大小为16MB。
- Modules区域:0xffff_7fff_c000_0000到0xffff_8000_0000_0000,大小为64MB。
- normal memory线性映射区:0xffff_8000_0000_0000到0xffff_ffff_ffff_ffff,大小为128TB。
根据地址划分可以看出出问题的内核地址空间在vmemmap区域
vmemmap区域
vmemmap是内核中page 数据的虚拟地址。针对sparse内存模型。内核申请page获取的page地址从此开始
异常内存访问导致的oops:
1、Unable to handle kernel paging request at virtual address 00000000
=====》越出内核地址空间范围,原因是由于使用空NULL指针
2、Unable to handle kernel paging request at virtual address 20100110
=====》越出内核地址空间范围,原因是的内存越界导致该指针
所在内存被破坏了。 接下来的困难是在什么地方这个内存被修改?为什么被修改?
3、Unable to handle kernel paging request at virtual address c074838c
=====》没有越出内核地址空间范围,为什么也oops?
这种情况我称之为:试图篡改受限制内存。比如:声明为const的变量!
还有其它形式的受限制内存吗?
三、访问受限制内存导致oops:
const在C语言当中声明一个变量为只读,
如果试图直接修改const变量,build阶段编译器,就检查出来,并报read only错误,
如下:
const int i = 1;
i = 10;
build error: assignment of read-only variable 'i' //只读变量赋值错误
但是如果通过指针间接修改const变量,编译器是检查不出来的。
如下:
const int i = 1;
int *p = &i;
*p = 10;
不出所料地编译成功了! 但不要高兴,这样的代码是有隐患的!
因为,很显然,我们将变量声明为const,是希望它能受到保护的!
既然编译器检查不出这种隐患,由谁负责保护它呢?
我想,linux只有运行时,由mm模块来保护声明为const的变量了!!!???
但遗憾的是,linux 3.4.5以前的版本一直没有这个保护功能,应该老版本linux自身的漏洞吧!
直到约linux 3.4.67 (android 4.4)才有运行时保护受限制内存的功能。