将物理地址映射到内核空间中的虚拟模拟系统

时间:2012-07-10 20:51:53

标签: c linux kernel mmap qemu

我正在使用基于QEMU的x86模拟器MARSSx86。我正在尝试在客户操作系统和模拟器之间发送一些数据。通常,这是通过使用开发人员提供的库来处理的,该库写入为此类通信保留的内存中的特殊区域。但是,我正在尝试在内核空间中执行此操作,并且库依赖于执行mmap,这显然不起作用。据我了解,我需要做的就是访问已知的物理地址并在那里写一些数据。

我对内存操作非常不熟悉,特别是在内核中,但我觉得这应该是直截了当的。我相信我可以使用ioremap,但它并没有像我理解的那样提供可用的虚拟地址,而实现的代码需要它。为了使事情更清楚,这里是设置映射的代码:

#define bits(x, i, l) (((x) >> (i)) & bitmask(l))
#define LO32(x) (W32)((x) & 0xffffffffLL)
#define bitmask(l) (((l) == 64) ? (W64)(-1LL) : ((1LL << (l))-1LL))

static int ptlsim_check_status __attribute__((common)) = 0;
static W64 supported_ptlcall_methods __attribute__((common)) = 0;
static int selected_ptlcall_method __attribute__((common)) = -1;
static W64 ptlcall_mmio_page_physaddr __attribute__((common)) = 0;
static W64* ptlcall_mmio_page_virtaddr __attribute__((common)) = NULL;
static W16 ptlcall_io_port __attribute__((common)) = 0;

static int ptlsim_ptlcall_init() {
  W32 rax = PTLSIM_CPUID_MAGIC;
  W32 rbx = 0;
  W32 rcx = 0;
  W32 rdx = 0;
  int ptlcall_mmio_page_offset;
  static const char* mmap_filename = "/dev/mem";

  // a.k.a. cpuid(PTLSIM_CPUID_MAGIC, rax, rbx, rcx, rdx);
  asm volatile("cpuid" : "+a" (rax), "+b" (rbx), "+c" (rcx), "+d" (rdx) : : "memory");

  if (rax != PTLSIM_CPUID_FOUND) {
    ptlsim_check_status = -1;
    return 0;
  }

  supported_ptlcall_methods = rbx;
  ptlcall_mmio_page_physaddr = (bits(rdx, 0, 16) << 32) | LO32(rcx);
  ptlcall_mmio_page_offset = (ptlcall_mmio_page_physaddr & 0xfff);
  ptlcall_mmio_page_physaddr &= ~0xfff;
  ptlcall_io_port = bits(rdx, 16, 16);

  if (supported_ptlcall_methods & PTLCALL_METHOD_MMIO) {
    // We use O_SYNC to guarantee uncached accesses:
    int fd = open(mmap_filename, O_RDWR|O_LARGEFILE|O_SYNC, 0);

    if (fd < 0) {
      fprintf(stderr, "ptlsim_ptlcall_init: cannot open %s for MMIO to physaddr %p (%s)\n",
      mmap_filename, (void*)ptlcall_mmio_page_physaddr, strerror(errno));
      supported_ptlcall_methods &= ~PTLCALL_METHOD_MMIO;
      ptlsim_check_status = -2;
      return 0;
    }

    ptlcall_mmio_page_virtaddr = (W64*)mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, ptlcall_mmio_page_physaddr);

    if (((int)(Waddr)ptlcall_mmio_page_virtaddr) == -1) {
      fprintf(stderr, "ptlsim_ptlcall_init: cannot mmap %s (fd %d) for MMIO to physaddr %p (%s)\n", mmap_filename, fd, (void*)ptlcall_mmio_page_physaddr, strerror(errno));
      supported_ptlcall_methods &= ~PTLCALL_METHOD_MMIO;
      ptlsim_check_status = -3;
      close(fd);
      return 0;
    }

    // Adjust the pointer to the actual trigger word within the page (usually always offset 0)
    ptlcall_mmio_page_virtaddr = (W64*)(((Waddr)ptlcall_mmio_page_virtaddr) + ptlcall_mmio_page_offset);

    close(fd);

    selected_ptlcall_method = PTLCALL_METHOD_MMIO;
    fprintf(stderr, "ptlsim_ptlcall_init: mapped PTLcall MMIO page at phys %p, virt %p\n", (void*)ptlcall_mmio_page_physaddr, (void*)ptlcall_mmio_page_virtaddr);
  }

  ptlsim_check_status = +1;

  return 1;
}

这是设置完成后进行实际通信的代码(为了简洁起见,我显然在这里留下了很多东西,但是如果缺少重要的东西,请告诉我,我可以编辑它):

static inline W64 ptlcall_single(const char* command, int flush) {
  struct  PTLsimCommandDescriptor desc;
  desc.command = (W64)command;
  desc.length = strlen(command);

  return ptlcall(PTLCALL_ENQUEUE, (W64)&desc, 1, flush, 0, 0, 0);
}

static inline W64 ptlcall(W64 op, W64 arg1, W64 arg2, W64 arg3, W64 arg4, W64 arg5, W64 arg6) 
{
  if (!is_running_under_ptlsim())
    return (W64)(-ENOSYS);

  if (selected_ptlcall_method == PTLCALL_METHOD_MMIO) {
    return do_ptlcall_mmio(op, arg1, arg2, arg3, arg4, arg5, arg6);
  } else
    return (W64)(-ENOSYS);
}

static inline W64 do_ptlcall_mmio(W64 callid, W64 arg1, W64 arg2, W64 arg3, W64 arg4, W64 arg5, W64 arg6)
{
  W64 rc;
  asm volatile ("movq %[arg4],%%r10\n"
            "movq %[arg5],%%r8\n"
            "movq %[arg6],%%r9\n"
            "mfence\n"
            "smsw %[target]\n"
            : "=a" (rc),
              [target] "=m" (*ptlcall_mmio_page_virtaddr)
            : [callid] "a" (callid),
              [arg1] "D" ((W64)(arg1)),
              [arg2] "S" ((W64)(arg2)),
              [arg3] "d" ((W64)(arg3)),
              [arg4] "g" ((W64)(arg4)),
              [arg5] "g" ((W64)(arg5)),
              [arg6] "g" ((W64)(arg6))
            : "r11","rcx","memory" ,"r8", "r10", "r9");
  return rc;
}

基本上,我想将所有这些代码放在内核中,然后在第一块代码中重写内存操作,以便第二块代码可以不加修改地运行。从我发现我觉得remap_pfn_range可能是我需要的功能,但我无法弄清楚如何在这种情况下使用它。我意识到这很多,所以即使他们没有回答整个问题,任何指针都会受到赞赏。

0 个答案:

没有答案