inline int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);
inline int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);
inline int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
inline int pci_write_config_byte(struct pci_dev *dev, int where, u8 val);
inline int pci_write_config_word(struct pci_dev *dev, int where, u16 val);
inline int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);
根本就是调用 pci总线接口
int pci_bus_read_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 *val); //读字节
int pci_bus_read_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 *val); //读字
int pci_bus_read_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 *val); //读双字
int pci_bus_write_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 val); //写字节
int pci_bus_write_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 val); //写字
int pci_bus_write_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 val); //写双字
在drivers\pci\access.c 中
int pci_bus_write_config_##size \
(struct pci_bus *bus, unsigned int devfn, int pos, type value) \
{ \
int res; \
unsigned long flags; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
pci_lock_config(flags); \
res = bus->ops->write(bus, devfn, pos, len, value); \ // 重点
pci_unlock_config(flags); \
return res; \
}
所以主要代码是 : bus->ops
驱动层代码接口
根总线读写操作接口初始化
pci_acpi_scan_root
->acpi_pci_root_create // 重要参数: acpi_pci_root_ops
->pci_create_root_bus // bridge->ops=acpi_pci_root_ops.pci_ops, 也就是 pci_root_ops
->pci_register_host_bridge
->bus->ops = bridge->ops // acpi_pci_root_ops.ops, 也就是 pci_root_ops
所以bus得操作接口为 acpi_pci_root_ops
struct pci_ops pci_root_ops = {
.read = pci_read,
.write = pci_write,
};
static struct acpi_pci_root_ops acpi_pci_root_ops = {
.pci_ops = &pci_root_ops,
};
所以只需要分析 pci_root_ops即可
int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 val)
{
if (domain == 0 && reg < 256 && raw_pci_ops)
return raw_pci_ops->write(domain, bus, devfn, reg, len, val); // 基础配置空间
if (raw_pci_ext_ops)
return raw_pci_ext_ops->write(domain, bus, devfn, reg, len, val); // 扩展配置空间
return -EINVAL;
}
所以可以得到结论:
pci读写 0x00-0xFF 使用raw_pci_ops
pci读写 0x100-0xFFF 使用 raw_pci_ext_ops
驱动层接口与原理
相关参考
基础知识
在X86架构上有关于这部分的描述:
10th Generation Intel Core Processors, Datasheet Volume 1 of 2 中 P29页描述:
PCI Express 将配置空间扩展到每个设备/功能4K字节。
PCI Express 配置空间分为 一个PCI兼容区域(就是前256个字节组成)和 一个扩展的PCIExpress 区域(就是 0x100-0xFFF)。
PCI前256字节配置空间:可以通过 PCI规范中定义的机制(就是 通过 0cf8-0cff : PCI conf1 两个ioport通过BDF来寻址访问 ) 或 使用PCI Express 增强配置机制(ECAM- PCI Express Enhanced Configuration Access Mechanism)访问机制来访问PCI兼容区域。
PCI的0x100-0xFFF的ECAM访问,使用ioremap去访问PCI Express区域,这个属于硬件支持,基地址从ACPI来获取到。
PCI Express 主机桥,将内存映射的PCI Express 配置空间访问从主机处理器转换为PCI Express 配置周期。为了保持与PCI配置寻址机制的兼容性,建议系统软件仅使用32位操作(32位对齐)访问增强的配置空间。有关PCI兼容和PCI Express 增强配置机制和事务规则的详细信息,请参阅《 PCI Express基本规范》。
raw_pci_ops 得初始化
初始化入口
// init.c:45:arch_initcall(pci_arch_init);
pci_arch_init
// 《Linux那些事之PCI》P5中描述了三种PCI access mode,
// 内核中CONFIG_PCI_DIRECT这个宏有配直接Direct去访问
pci_direct_probe(); // 初始化0xCF8和0xCFC,并初始化
if (x86_init.pci.arch_init && !x86_init.pci.arch_init()) // 函数没实现,哈哈
return 0;
// pci_pcbios_init(); // CONFIG_PCI_BIOS--不配置,不用看
pci_direct_init(type); // raw_pci_ops = raw_pci_ext_ops 预留读写pci配置空间的接口
pci_probe & PCI_PROBE_CONF1 # 判断,什么是CONF1和CONF2
request_region(0xCF8, 8, "PCI conf1") # 为什么使用0xCF8
pci_check_type1() # 检测type1
raw_pci_ops = &pci_direct_conf1; # !!! 初始化pci配置空间操作接口
结论(重点):
所以raw_pci_ops = &pci_direct_conf1; 可以看到 使用0xCF8和0xCFC访问PCI基础配置空间
inno@DEV-005:~$ sudo cat /proc/ioports | grep "PCI conf" # 申请到得IO接口
0cf8-0cff : PCI conf1
// arch/x86/direct.c中 // 访问格式
#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
(0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
| (devfn << 8) | (reg & 0xFC))
outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8); // 配置地址
u32 value = inl(0xCFC); // 读取配置
在深入PCI与PCIe之二:软件篇 中描述了CONFIG_ADD_RESS得结构:
CONFIG_ADDRESS寄存器格式:
31 位:Enabled位。
23:16 位:总线编号。 // bus
15:11 位:设备编号。 // devfn[7:3]
10: 8 位:功能编号。 // devfn[2:0]
7: 2 位:配置空间寄存器编号。 // 配置空间偏移地址, 注:因为是32位端口,所以4字节访问。
1: 0 位:恒为“00”。这是因为CF8h、CFCh端口是32位端口。
raw_pci_ext_ops 得原理-ECAM
参考:
ECAM是访问PCIe配置空间一种机制,PCIe配置空间大小是4k
4kbyte寄存器地址空间,需要12bit bit 0~bit11
Function Number bit 12~bit 14
Device Number bit 15~bit 19
Bus Number bit 20~bit 27
如何访问一个PCIe设备的配置空间呢?
比如ECAM 基地址是0xd0000000
devmem 0xd0000000就是访问00:00.0 设备偏移0寄存器,就是Device ID和Vendor ID
devmem 0xd0100000就是访问01:00.0 设备偏移0寄存器
所以,这里重点就是看ECAM得初始化
pci_acpi_scan_root // 主桥信息struct pci_root_info 和 struct pci_sysdata 初始化
-> acpi_pci_root_create // ECAM初始化,主桥资源初始化
-> ops->init_info(info) // 就是 acpi_pci_root_ops得 pci_acpi_root_init_info
pci_acpi_root_init_info
-> setup_mcfg_map
setup_mcfg_map
pci_mmcfg_late_init();
// #define ACPI_SIG_MCFG "MCFG" /* PCI Memory Mapped Configuration table */
acpi_table_parse(ACPI_SIG_MCFG, pci_mcfg_parse); // "ACPI中关于MCFG的描述"
if (raw_pci_ext_ops == NULL)
raw_pci_ext_ops = &pci_mmcfg; // !!! 初始化接口
如何获取ECAM得基地址?
方式一:打印ACPI表
sudo apt-get install -y iasl acpica-tools
mkdir -p testacpi && cd testacpi
acpidump > acpidump.out # 将ACPI表二进制打印到文件
acpixtract -a acpidump.out # 解析acpi表,生成各个dat文件
iasl -d mcfg.dat # iasl会解析acpi 二进制表,生成xxx.dsl描述文件
cat mcfg.dsl # 可以查看mcfg的配置文件
比如Intel,我这里看到的是 0x8000_0000,
Start BusNum=00, End BusNum=ff, 所以所有总线的ECAM都在这个空间,按照ECAM地址空间依次偏移即可。
cat /proc/ioport | grep 80000000
80000000-8FFFFFFF PCI MMCONFIG [bus 00 - ff]
方式二:看内核启动打印
比如这个ECAM的基地址是0xe0000000
[ 0.111732] PCI: MMCONFIG for domain 0000 [bus 00-ff] at [mem 0xe0000000-0xefffffff] (base 0xe0000000)
[ 0.111734] PCI: MMCONFIG at [mem 0xe0000000-0xefffffff] reserved in E820
设置开机启动和后台运行同样的道理,我们也需要对客户端设置后台运行和开机自启。借助 winsw 工具可以将frpc注册为windows系统中的服务。下载winsw最新版,为了方便将其重命名为winsw.exe, 将该文件和frpc.exe放在一起,然后新建winsw.xml写入以下内容:<service> <id>frp</id> <name>frp</name> <description>用frp发布本地电_-services.msc-frpc-
图像搜索引擎一般有三种实现方式:(1)Search By Metadata,这种方式不会考虑图片本身内容(图片包含物体,以及图像像素分布等),纯粹根据图像标签来进行检索。如果某个网页中有一张赛马的图片,并且网页文本内容中包含“赛马”(或者相关词汇)的文字,当用户搜索“赛马”、“马”、“horse”等关键字时,搜索引擎就会把这张图当作检索结果返回给用户。换句话说,此时的图像搜索引擎干的事情跟普通搜索引擎差不多,匹配关键词,并将对应图片返回给用户。这种工作方式的优点是速度快,在普通搜索引擎的技术基础之上很容._python+flask制作图像检索系统
1、上传下载好的安装包2、创建kettle和组和用户,并使kettle用户属于kettle组,给kettle设置密码[root@localhost ~]# groupadd kettle[root@localhost ~]# useradd -r -g kettle kettle[root@localhost ~]# passwd kettle3、解压上传好的kettle包[root@localhost ~]# unzip pdi-ce-7.1.0.0-12.zip4、授权1)将这个文_kettle在linux安装使用
3. 实验过程1:先用指令“ifconfig”虚拟机ip,我的是192.168.85.128再用指令“nmap 192.168.85.1-255”寻找出靶机ip:192.168.85.1332.可以看到端口开放有:端口22,22端口是ssh服务,端口22可以实现远程连接端口80,80端口是HTTP服务,80是为http协议开放的上图可以查询到靶机IP,用火狐浏览器访问刚开始点开觉得没啥有用信息,然后看到下面有“log in”点进去是需要账号密码登入的.._信安渗透
<template><div><el-select v-model=“fruit” @change=“handleChange”><el-option v-for=“item in selectList” :key=“item.whsCode” :label=“item.fruitName” :value=“item.fruitEnName”></el-option></el-select></div></_vue select 获取选中的子级value
该博客只是自己记录学习所用,建议大家观看凉鞋大神的教程http://qf.liangxiegame.com/qf/communityusing System.Collections;using System.Collections.Generic;using UnityEngine;namespace QF.Guide{ public class HelloQF..._qframework v1.0下载
Java 基础部分:OOP 概念抽象类与接口构造函数与 initialization order(初始化顺序)Java 中的一些关键字:static、final、 volatile、synchronized、transient、this 等等File I/O 和序列化Collections:List、Map、Set异常泛型JVM 和内存管理多线程和同
ASP.NET MVC+EF框架+EasyUI实现权限管理系列(11)-验证码实现和底层修改 原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(11)-验证码实现和底层修改 ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇)(1):框架搭建(2):数据库访问层的设计Dem..._easyui设置验证码
问题:python多进程,子函数内容没有打印出来。Simple Python Multiprocessing function doesn't output results I have this very simple function right here in which I'm trying to run and test on, however, it doesn't out..._python进程池中print无效
世界编程语言排行榜08年06月 TIOBE Programming Community Index for April 2008 注: TIOBE 世界编程语言排行榜展现了编程语言的流行趋势。每个月,都有最新的数据被更新。这份排行榜的数据取样来源于互联网上富有经验的程序员、商业应用、著名的搜 索引擎(诸如谷歌、 MSN 、雅虎)的关键字排名、 Alexa 上的排名等。请注意这个排行榜只是反映了某个_编程语言流行度排名 2008年
定期送福利,今天给大家送上Windows中利用DirectShow采集microphone音频,并将采集到的pcm数据,利用FAAC库编码成AAC,进行本地存储或者网络传输。直接贴代码,解析看注释:/* 功能描述: 简单实现用DirectShow采集Windows麦克风音频PCM数据 经过FAAC编码成AAC存储文件,可根据项目修改成直播,对讲 等实时..._dsaudio 音频采集
省市县插件PCASClass.js_pcasclass