/*
 * DF-2675 p16watch: for every OVR buffer (xio_npages > kvasize/4096),
 * print kva, npages, loff, and the first 16 bytes of the page currently
 * mapped at kva+MAXBSIZE (the overflow page) -- lets the orchestrator
 * detect when a neighbor replaced the mapping (content becomes canary).
 */
#include <sys/types.h>
#include <err.h>
#include <fcntl.h>
#include <kvm.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define PMASK 0x000FFFFFFFFFF000ULL
#define MAXBSIZE_ 65536

static int memfd;
static uint64_t KPML4;

static int
rdphys(uint64_t pa, void *buf, size_t len)
{
	return (pread(memfd, buf, len, (off_t)pa) == (ssize_t)len);
}

static uint64_t
pteof(uint64_t va)
{
	uint64_t pml4[512], pud[512], pd[512], pt[512];
	int i4, i3, i2;

	i4 = (int)((va >> 39) & 511);
	i3 = (int)((va >> 30) & 511);
	i2 = (int)((va >> 21) & 511);
	if (!rdphys(KPML4, pml4, 4096) || !(pml4[i4] & 1)) return 0;
	if (!rdphys(pml4[i4] & PMASK, pud, 4096) || !(pud[i3] & 1)) return 0;
	if (pud[i3] & 0x80) return 0;
	if (!rdphys(pud[i3] & PMASK, pd, 4096) || !(pd[i2] & 1)) return 0;
	if (pd[i2] & 0x80) return 0;
	if (!rdphys(pd[i2] & PMASK, pt, 4096)) return 0;
	return pt[(va >> 12) & 511];
}

int
main(void)
{
	kvm_t *kd;
	struct nlist nl[] = { { "_buf" }, { "_KPML4phys" }, { NULL } };
	unsigned char *arr;
	long nbuf = 12634, szbuf = 1352, n;
	uint64_t bufva;

	kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "p16watch");
	if (!kd) return 1;
	if (kvm_nlist(kd, nl) != 0) errx(1, "nlist");
	if (kvm_read(kd, nl[1].n_value, &KPML4, 8) != 8) errx(1, "kpml4");
	if (kvm_read(kd, nl[0].n_value, &bufva, 8) != 8) errx(1, "bufptr");
	arr = malloc(nbuf * szbuf);
	if (!arr) err(1, "malloc");
	if (kvm_read(kd, bufva, arr, nbuf * szbuf) != nbuf * szbuf) errx(1, "arr");
	kvm_close(kd);

	memfd = open("/dev/mem", O_RDONLY);
	if (memfd < 0) err(1, "/dev/mem");

	for (n = 0; n < nbuf; n++) {
		uint64_t kv, loff, p16;
		int np, ks;
		unsigned char rec[1352], data[16];
		memcpy(rec, arr + n * szbuf, szbuf);
		memcpy(&kv, rec + 1024, 8);
		if (kv == 0) continue;
		memcpy(&np, rec + 1056, 4);
		memcpy(&ks, rec + 1032, 4);
		if (ks == 0) continue;
		if (np <= ks / 4096) continue;
		memcpy(&loff, rec + 176, 8);
		p16 = pteof(kv + MAXBSIZE_) & PMASK;
		memset(data, 0, 16);
		if (p16) rdphys(p16, data, 16);
		printf("OVR n=%ld kva=%#llx np=%d loff=%#llx p16=%#llx c=%02x%02x%02x%02x %c%c%c%c%c%c%c%c\n",
		       n, (unsigned long long)kv, np,
		       (unsigned long long)loff, (unsigned long long)p16,
		       data[0], data[1], data[2], data[3],
		       (data[0] > 32 && data[0] < 127) ? data[0] : '.',
		       (data[1] > 32 && data[1] < 127) ? data[1] : '.',
		       (data[2] > 32 && data[2] < 127) ? data[2] : '.',
		       (data[3] > 32 && data[3] < 127) ? data[3] : '.',
		       (data[4] > 32 && data[4] < 127) ? data[4] : '.',
		       (data[5] > 32 && data[5] < 127) ? data[5] : '.',
		       (data[6] > 32 && data[6] < 127) ? data[6] : '.',
		       (data[7] > 32 && data[7] < 127) ? data[7] : '.');
	}
	return 0;
}
