[Solaris-Driver]Device Address Mapping

本文详细介绍了设备驱动中使用ddi_regs_map_setup建立内核虚拟地址与设备地址空间之间的映射过程,包括如何通过rnumber索引到reg属性中的不同内存区域,并通过实例展示了配置寄存器值的读取方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Device Address Mapping

 

Device drivers use ddi_regs_map_setup(9F) to establish mappings between kernel virtual address (kvaddr) and device address space for PIO. A device address space may consist of multiple addressable memory regions. Each region is assigned a register set number, rnumber. ddi_dev_nregs(9F) gives the total number of register sets that a device has.

The "reg" property shows the number of addressable regions exported by the device to the system. ddi_regs_map_setup(9F) uses rnumber to index into the array of registers in the "reg" property and returns a kvaddr mapped to the region corresponding to rnumber.

As mentioned in "IEEE 1275 PCI Binding," IEEE 1275 PCI binding makes no specific connection between the entries in the "reg" property and the configuration registers in PCI configuration space. Driver developers should check the "reg" property to ensure that the correct rnumber is used in ddi_regs_map_setup(9F).

For example, the "reg" property of /pci@1f,4000/scsi@3 shown in the example of the PCI SCSI device node properties contains four register sets: one for configuration space, one for I/O space, and two for memory space.

The "assigned-addresses" property has three register sets: one for I/O space, and two for memory space, as shown below.

assigned-addresses 81001810 00000000 00000400 00000000 00000100

82001814 00000000 00018000 00000000 00000100

82001818 00000000 00019000 00000000 00001000

reg 00001800 00000000 00000000 00000000 00000000 <-- rnumber 0

01001810 00000000 00000000 00000000 00000100 <-- rnumber 1

02001814 00000000 00000000 00000000 00000100 <-- rnumber 2

02001818 00000000 00000000 00000000 00001000 <-- rnumber 3

If you type show-pci-config at the ok prompt of OpenBoot, you get the values of the device's configuration registers, as shown in this example of the configuration register values for an on-board SCSI device. As shown below, base address 0 is for I/O space and base address 1 and 2 are for memory space.

ok show-pci-config /pci@1f,4000/scsi@3

PCI Header for Bus # 0, Dev # 3, Func # 0


Vendor ID = 1000

Device ID = f

Command Reg = 46

Status Reg = 200

Revision ID = 1

Class Code = 10000

Cache Line Size = 10

Latency Timer = 40

Header Type = 0

BIST = 0

Base Address 0 = 401

Base Address 1 = 18000

Base Address 2 = 19000

Base Address 3 = 0

Base Address 4 = 0

Base Address 5 = 0

Base Address ROM = 0

Interrupt Line = 0

Interrupt Pin = 1

Minimum Grant = 8

Maximum Latency = 40

If you write a routine to map into register set 0, as shown below, you should be able to read the device's configuration space and get the same value of device ID, vendor ID, and Class Code as shown in the example of on-board SCSI configuration register values.

#define CONFIG_VENDOR_ID_OFFSET 0x0

#define CONFIG_DEVICE_ID_OFFSET 0x2

#define CONFIG_CLASS_CODE_OFFSET 0x8

#define CONFIG_BASE_ADDR_2_OFFSET 0x18

#define CONFIG_REGS_SIZE 0x40



static struct ddi_device_acc_attr endian_attr = {

DDI_DEVICE_ATTR_V0,

DDI_STRUCTURE_LE_ACC,

DDI_STRICTORDER_ACC

};



xxx_map_regs(dev_info_t *dip) {

caddr_t configp; /* pointer to configuration space */

caddr_t mem_reg1; /* pointer to memory space 1 */

size_t mem_size; /* size of memory space as shown in "reg" */

ddi_acc_handle_t handle0; /* Access handle for configuration space */

ddi_acc_handle_t handle2; /* Access handle for memory space 1 */

ulong_t base_addr2; /* Base address 1 value (rnumber = 2) */

ushort_t vendor_id, device_id;

uint_t class_code;

......



/*

* configp is the kernel virtual address that maps to the device

* configuration space, rnumber is 0.

*/

ddi_regs_map_setup(dip, 0, &configp, 0, CONFIG_REGS_SIZE,

&endian_attr, &handlep0);

/*

* Comparing with the corresponding properties,

*

* vendor_id should be 0x00001000.

* device_id should be 0x0000000f.

* class_code should be 0x00010000.

* baddr1 should be 18000.

*/

vendor_id = ddi_getw(handle0, configp + CONFIG_VENDOR_ID_OFFSET);

device_id = ddi_getw(handle0, configp + CONFIG_DEVICE_ID_OFFSET);

base_addr2 = ddi_getl(handle0, configp + CONFIG_BASE_ADDR_2_OFFSET);

class_code = (uint_t)ddi_getl(handle0, configp + CONFIG_CLASS_CODE_OFFSET);

class_code = (uint_t)(class_code >> 8);

......



> /*

* memp is the kernel virtual address that maps to the device memory space

* register number 2, rnumber = 2.

*/

ddi_dev_regsize(dip, 2, &mem_size);

ddi_regs_map_setup(dip, 2, &mem_reg1, 0, mem_size, &endian_attr,

&handlep2);

......

}

In the example above, mem_reg1 and base_addr2 are referring to the same memory object on the device. mem_reg1 is the kvaddr mapped to the memory object and can be used by the driver to access that memory object.

mem_reg1 is allocated from the kernel resources map (kernelmap) and has the value between SYSBASE and SYSEND, as shown in the kernel address map below.

base_addr2 contains the value of the base address register for memory space 1. In this example, the value of base_addr2 is 18000. base_addr2 is the physical address of the memory object in the PCI bus address domain. The system uses base_addr2 in ddi_regs_map_setup(9F) to establish mapping between mem_reg1 and rnumber.

In the ddi_regs_map_setup(9F) call, the system traverses the device tree all the way up to the root nexus driver to establish mapping between kernel virtual address (for example, mem_reg1) and the device register address space denoted by rnumber. On the way to the root nexus driver, the system calls the PCI nexus driver, the device's parent node, to convert rnumber to a generic register structure that can be used by the root nexus driver. The PCI nexus driver reads the values of the "reg" property of the device (if the address in the "reg" property is relocatable, the PCI nexus driver gets the values from the "assigned-addresses" property) and uses rnumber to index into the array to construct the register structure. Inside the register structure is a field that contains the physical address of the device register in the PCI bus address domain (for example, the value of base_addr2).

The system passes the register structure to the root nexus driver. The root nexus driver does three things:

  • Converts the address in the register structure into a physical address (paddr) in the system bus domain
  • Allocates a kvaddr from kernelmap
  • Establishes a mapping between kvaddr and paddr

The root nexus driver then returns the kvaddr to the device driver. When the kvaddr is accessed, the system generates a memory fault to load the system MMU with the kvaddr->paddr translations. It should be noted that the IOMMU of the HPB is not involved in the translations.

 
资源下载链接为: https://pan.quark.cn/s/d9ef5828b597 在本文中,我们将探讨如何通过 Vue.js 实现一个带有动画效果的“回到顶部”功能。Vue.js 是一款用于构建用户界面的流行 JavaScript 框架,其组件化和响应式设计让实现这种交互功能变得十分便捷。 首先,我们来分析 HTML 代码。在这个示例中,存在一个 ID 为 back-to-top 的 div 元素,其中包含两个 span 标签,分别显示“回到”和“顶部”文字。该 div 元素绑定了 Vue.js 的 @click 事件处理器 backToTop,用于处理点击事件,同时还绑定了 v-show 指令来控制按钮的显示与隐藏。v-cloak 指令的作用是在 Vue 实例渲染完成之前隐藏该元素,避免出现闪烁现象。 CSS 部分(backTop.css)主要负责样式设计。它首先清除了一些默认的边距和填充,对 html 和 body 进行了全屏布局,并设置了相对定位。.back-to-top 类则定义了“回到顶部”按钮的样式,包括其位置、圆角、阴影、填充以及悬停时背景颜色的变化。此外,与 v-cloak 相关的 CSS 确保在 Vue 实例加载过程中隐藏该元素。每个 .page 类代表一个页面,每个页面的高度设置为 400px,用于模拟多页面的滚动效果。 接下来是 JavaScript 部分(backTop.js)。在这里,我们创建了一个 Vue 实例。实例的 el 属性指定 Vue 将挂载到的 DOM 元素(#back-to-top)。data 对象中包含三个属性:backTopShow 用于控制按钮的显示状态;backTopAllow 用于防止用户快速连续点击;backSeconds 定义了回到顶部所需的时间;showPx 则规定了滚动多少像素后显示“回到顶部”按钮。 在 V
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值