24/05/2018, 18:37

Các cổng và các thanh ghi (Ports and Registers )

Windows XP sử dụng mô hình máy tính trừu tượng như được mô tả trong hình 7.4 để cung cấp một giao diện driver hợp nhất như trong kiến trúc của CPU. Trong chế độ này, một CPU có thể có bộ nhớ riêng biệt và các không gian địa chỉ. Để truy cập vào bộ nhớ ...

Windows XP sử dụng mô hình máy tính trừu tượng như được mô tả trong hình 7.4 để cung cấp một giao diện driver hợp nhất như trong kiến trúc của CPU. Trong chế độ này, một CPU có thể có bộ nhớ riêng biệt và các không gian địa chỉ. Để truy cập vào bộ nhớ thiết bị, CPU sẽ giao công việc cho một dạng bộ nhớ khác như là một bộ nhớ load hay lưu truy cập trực tiếp đến một địa chỉ ảo. CPU dịch địa chỉ ảo đó ra thành địa chỉ vật lý bằng cách thiết lập các bảng trang. Để truy cập vào một thiết bị vào ra đã được sơ đồ hoá, bằng một cách khác, CPU cầu cứu tới một hệ thống đặc biệt là x86 với lời chỉ dẫn vào và ra.

Hình 7.4. Truy cập vào các cổng và các thanh ghi.

Các thiết bị phải có đường Bus là các cách thức cụ thể để có thể giải mã bọ nhớ và các địa chỉ vào/ra. Trong trường hợp bus PCI, một cầu nối chính (host) để vẽ ra đường đi (nối) giữa địa chỉ bộ nhớ vật lý của CPU và các điạ chỉ vào/ra tới không gian địa chỉ cái mà có thể truy cập trực tiếp tới các thiết bị. Bít cờ trong không gian cấu hình của thiết bị sẽ quyết định cầu nối này có vẽ được đường đi (nối từ) từ các thanh ghi của thiết bị tới bộ nhớ hoặc địa chỉ vào/ra trên CPU thong qua cả hai không gian địa chỉ.

Rộng hơn khi bạn di chuyển có điều kiện một loạt code được biên dịch trong trình điều khiển của bạn cho tất cả các thể hiện có thể, các nhà thiết kế Windows Nt đã sáng tạo ra một lớp các phần cứng trừu tượng (Hal), cái mà tôi đã ám chỉ một vài lần trong cuốn sách này. Hal cung cấp các hàm cái mà bạn sử dụng để truy xuất tới các tài nguyên bộ nhớ và các cổng.

Hãy nhìn bảng 7.3. Với bảng tóm lược này, bạn có thể đọc/ghi hoặc là UCHAR/USHORT/ULONG hoặc là một mảng các kiểu này đến hay từ (đến và đi) một Cổng//Thanh ghi. điều này tạo nên 24 hàm hal tất cả mà bạn sử dụng để truy cập các thiết bị. Từ trình điều khiển WDM không trực tiếp dựa vào Hal cho tất cả mọi thứ, bạn có thể nghĩ về 24 hàm này tốt như là việc công khai toàn bộ, nguyên vẹn …… hal.

Bảng 7.3. Các hàm Hal cho việc truy cập các cổng và các thanh ghi bộ nhớ.

Table 7-3. HAL Functions for Accessing Ports and Memory Registers
Access Width Functions for Port Access Functions for Memory Access
8 bits READ_PORT_UCHAR WRITE_PORT_UCHAR READ_REGISTER_UCHAR WRITE_REGISTER_UCHAR
16 bits READ_PORT_USHORT WRITE_PORT_USHORT READ_REGISTER_USHORT WRITE_REGISTER_USHORT
32 bits READ_PORT_ULONG WRITE_PORT_ULONG READ_REGISTER_ULONG WRITE_REGISTER_ULONG
String of 8-bit bytes READ_PORT_BUFFER_UCHAR WRITE_PORT_BUFFER_UCHAR READ_REGISTER_BUFFER_UCHAR WRITE_REGISTER_BUFFER_UCHAR
String of 16-bit words READ_PORT_BUFFER_USHORT WRITE_PORT_BUFFER_USHORT READ_REGISTER_BUFFER_USHORT WRITE_REGISTER_BUFFER_USHORT
String of 32-bit ­doublewords READ_PORT_BUFFER_ULONG WRITE_PORT_BUFFER_ULONG READ_REGISTER_BUFFER_ULONG WRITE_REGISTER_BUFFER_ULONG

Những gì diễn ra bên trong của các hàm truy cập này là phục thuộc vào hệ nền(Platform). Ví dụ, phiên bản Intel x86 của READ_PORT_CHAR thực hiện một cấu trúc IN để đọc một byte từ cổng vào /ra đã được chỉ định. Bản bổ sung Microsoft Windows 98/ME tiến xa hơn ….. lưu trữ tràn lời gọi của trình điều khiển với một lời gọi IN hiện tại trong một số trường hợp. The Alpha version of this routine performs a memory fetch. Phiên bản intel x86 của Read_Register_uchar cũng thực hiện một lộ trình bộ nhớ. …. Theo cách khác, phiên bản định vùng đệm của hàm này (Read-registẻ-unchar) thực hiện thêm một số công việc trong môi trường x86 để chắc chắn rằngtất cả các bộ nhớ cache được xoá (làm sạch) một cách hợp lý khi mà hoạt động này kết thúc.

Tài nguyên cổng (Port)

Các thiết bị vào ra được mô hình hoá (IO-Mapped) phơi bày ra các thanh ghi phần cứng cái mà trên một số kiến trúc CPU (bao gồm cả Intel x86) được định địa chỉ thong qua phần mềm sử dụng một không gian địa chỉ vào ra đặc biệt. Trên một số kiến trúc CPU khác, không tồn tại không gian địa chỉ riêng biệt, và các thanh ghi này được định địa chỉ thong qua việc sử dụng một bộ nhớ chính thức khác. May mắn thay, bạn không cần phải hiểu rõ về sự phức tạp của các địa chỉ này. Nếu bạn yêu cầu thiết bị một cổng tài nguyên, một vòng lặp của bạn lặp qua các kí hiệu tài nguyên được dịch sẽ tìm kiếm một kí hiệu CmResourceTypePort, và bạn sẽ lưu thông tin ở 3 nơi.

typedef struct _DEVICE_EXTENSION {

  PUCHAR portbase;

  ULONG nports;

  BOOLEAN mappedport;

  } DEVICE_EXTENSION, *PDEVICE_EXTENSION;

PHYSICAL_ADDRESS portbase;    // base address of range

for (ULONG i = 0; i < nres; ++i, ++resource)

  {

  switch (resource->Type)

    {

  case CmResourceTypePort:

    portbase = resource->u.Port.Start;

    pdx->nports = resource->u.Port.Length;

    pdx->mappedport =

     (resource->Flags & CM_RESOURCE_PORT_IO) == 0;

    break;

    }

if (pdx->mappedport)

  {

  pdx->portbase = (PUCHAR) MmMapIoSpace(portbase,

    pdx->nports, MmNonCached);

  if (!pdx->portbase)

    return STATUS_NO_MEMORY;

  }

else

  pdx->portbase = (PUCHAR) portbase.QuadPart;

  1. Kí hiệu nguồn chứa đựng một tập hợp được đặt tên là u cái mà có các cấu trúc hạ tầng cho mỗi kiểu trong số các kiểu nguồn chuẩn. u.Port có các thong tin về một tài nguyên cổng. u.Port.Start là địa chỉ bắt đầu của một vùng liên tiếp các cổng vào ra, và u.Port.Length là số lượng các cổng trong vùng (phạm vi) đó. Địa chỉ bắt đầu là một giá trị địa chỉ vật lý 64 bit.
  2. Thành phần Flags của kí hiệu nguồn cho một cổng tài nguyên có cờ CM-RESOURCE-PORT-TO được thiết lập nếu như kiến trúc của CPU có không gian địa chỉ vào ra riêng bệt để chứa các địa chỉ cổng thuộc về nó.
  3. Nếu cờ CM_resource_Port_IO bị xoá, khi mà nó là một hệ nền của Alpha hoặc một hang khác như RISC, bạn cần phải gọi MnMapIoSpace để đạt được nhân của nó ở chế độ địa chỉ ảo bằng cổng có thể được truy cập. Việc truy cập này sẽ giao cho một bộ nhớ khác, nhưng bạn vẫn gọi cổng …. của các công việc thường nhật của Hal (read_Port_unchar và cứ thế) từ trình điều khiển.
  4. Nếu cờ CM_RESOURCE_PORT_IO được thiết lập, đó là trong hệ nền x86, thì bạn không cần vẽ sơ đồ (định sơ đồ) địa chỉ cổng. Bạn sẽ gọi PORT flavor of HAL routines từ trình điều khiển của bạn khi bạn muốn truy xuất vào một cổng trong số các cổng của bạn. Các công việc thường nhật của HAL (Hal routimes) đòi hỏi một đối số địa chỉ cổng Puchar, đólà lý do tại sao chúng ta loại được các đại chỉ cơ sở với kiểu này. Theo cách này thì một phần khác QuadPart dẫn đến (tạo ra) con trỏ 32 bit hoặc 64 bít, thích hợp với hệ nền cho cái mà bạn đang biên dịch.

- Nhờ StoptDevice của bạn sẽ giải phóng được tài nguyên thực hiện nếu bạn phải tiến hành sơ đồ hoá các tài nguyên cổng của bạn.

VOID StopDevice(...)

  {

  if (pdx->portbase && pdx->mappedport)

    MmUnmapIoSpace(pdx->portbase, pdx->nports);

  pdx->portbase = NULL;

  }

Tài nguyên bộ nhớ (Memory Resources):

Các thiết bị mà bộ nhớ được mô hình hoá chỉ ra các thanh ghi, cái mà phần mềm truy cập sử dụng câu lệnh nạp và lưu trữ. Giá trị tài nguyên được dịch này bạn lấy từ trình quản lý PnP là một địa chỉ vật lý, và bạn cần phải dự phòng các địa chỉ ảo để có thể chứa (bao bọc) bộ nhớ vật lý. Sau đó, bạn sẽ phải gọi các thủ tục của Hal, những cái mà sẽ lien kết với các thanh ghi bộ nhớ như là Read_Register_Unchar , write_register_unchar, và hơn thế nữa. Phần code cấu hình cũng như chú thích của bạn sẽ tương tự như những gì được trình bày dưới đây.

typedef struct _DEVICE_EXTENSION {

  PUCHAR membase;

  ULONG nbytes;

  } DEVICE_EXTENSION, *PDEVICE_EXTENSION;

PHYSICAL_ADDRESS membase;     // base address of range

for (ULONG i = 0; i < nres; ++i, ++resource)

  {

  switch (resource->Type)

    {

  case CmResourceTypeMemory:

    membase = resource->u.Memory.Start;

    pdx->nbytes = resource->u.Memory.Length;

    break;

    }

pdx->membase = (PUCHAR) MmMapIoSpace(membase, pdx->nbytes,

  MmNonCached);

if (!pdx->membase)

  return STATUS_NO_MEMORY;

  1. Trong suốt phần nguồn ở trên (code), u.memory có các thong tin vềtài nguyên bộ nhớ. U.memory.Start là địa chỉ bắt đầu của một khoảng (phạm vi) liên tiếp các vùng của bộ nhớ, và u.memory.length là số byte của cả phạm vi đó. Địa chỉ bắt đầu là một giá trị địa chỉ vật lý 64 bits. Không phải ngẫu nhiên mà u.port và u.memory là gống nhau- đó là một việc làm có mục đích và bạ có thể kiểm tra lại để thấy điều này đúng nếu bạn muốn
  2. Bạn phải gogị hàm MnMapÍopace để đạt được nhân chế độ địa chỉ ảo thong qua phạm vi bộ nhớ có thể được truy xuất.

Hàm StopDevice của bạn vạch ra vô điều kiện các tài nguyên bộ nhớ của bạn

VOID StopDevice(...)

  {

  if (pdx->membase)

    MmUnmapIoSpace(pdx->membase, pdx->nbytes);

  pdx->membase = NULL;

  }

0