/* * hack for acpi userspace battery for QEMU * Michael John Wensley * gcc -Wall -Wextra -o ioacpi ioacpi.c $(pkg-config --cflags --libs libsystemd) * * This deals with the simplest case that guest reads variable data from a file stored on the host * and discard any writes, although leave open the possibility in the ioport to ioacpi protocol * to react to writes for subaddressing, or settings changes. * * lowlevel test, boot guest, hexedit /dev/port, goto 0xff00 and read/write * highlevel test, goto /sys/bus/acpi/devices/PNP0C0A:00/power_supply/BAT1 * * test unsolicted writing back to ioport for interrupt injection: * * echo -e "call lseek(4,0,0)\ncall (int)sendfile(3,4,0,1)" | gdb -p "$(pidof ioacpi)" * * When linux is the guest, code of interest: * linux/drivers/acpi/battery.c - search for "_B", get _BIF _BIX _BST (set interrupts at guest) */ #define BUFFER_SIZE 12 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void connection(int data_socket, int file_socket) { int ret; int sofar; int request; unsigned char buffer[BUFFER_SIZE]; for (;;) { printf("wait data\n"); // block(data_socket); ret = read(data_socket, buffer, 1); if (ret == -1) { perror("read"); exit(EXIT_FAILURE); } if (ret > 0) { sofar = ret; //printf("aquire address length = %d\n", buffer[0]); while (sofar < (1 + buffer[0])) { //printf("incomplete read, so far = %ld\n", sofar); ret = read(data_socket, buffer + sofar, 1 + buffer[0] - sofar); //printf("read = %d\n", ret); if (ret == -1) { perror("read"); exit(EXIT_FAILURE); } sofar += ret; if (ret == 0) { return; } } if (0) { printf("have data %d %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", ret, buffer[0],buffer[1],buffer[2],buffer[3], buffer[4],buffer[5],buffer[6],buffer[7], buffer[8]); } //printf("read data octet\n"); ret = read(data_socket, buffer, 1); if (ret == 0) { return; } if (buffer[0] & 0x80) { if ((buffer[0] & 0x7f) > 0) { request = buffer[0] & 0x7f; u_int64_t addr = *((uint64_t*)&buffer[1]); printf("request for %d octets from address %ld\n", request, addr); buffer[0] = 0xEA; buffer[1] = 0xEA; buffer[2] = 0xEA; buffer[3] = 0xEA; buffer[4] = 0xEA; buffer[5] = 0xEA; buffer[6] = 0xEA; buffer[7] = 0xEA; pread(file_socket, buffer, request, addr ); write(data_socket, buffer, request ); } } else { if ((buffer[0] & 0x7f) > 0) { request = buffer[0] & 0x7f; u_int64_t addr = *((uint64_t*)&buffer[1]); printf("offer for %d octets from address %ld\n", request, addr); buffer[0] = 0xEA; buffer[1] = 0xEA; buffer[2] = 0xEA; buffer[3] = 0xEA; buffer[4] = 0xEA; buffer[5] = 0xEA; buffer[6] = 0xEA; buffer[7] = 0xEA; read(data_socket, buffer, request ); /* currently discard writes, could be enabled to test this: pwrite(file_socket, buffer, request, addr ); */ } } fflush(stdout); } else { return; } } } int main(int argc, char *argv[]) { int connection_socket; int data_socket; int file_socket; if (argc < 2) return EXIT_FAILURE; /* Create local socket. */ file_socket = open(argv[1], 0); if (file_socket == -1) { perror("cannot find acpi template file"); exit(EXIT_FAILURE); } int n; for (n = SD_LISTEN_FDS_START; n < (SD_LISTEN_FDS_START + sd_listen_fds(0)); n++ ) { if (sd_is_socket_unix(n, SOCK_STREAM, 0, NULL, 0)) { printf("have connected socket\n"); connection(n, file_socket); } if (sd_is_socket_unix(n, SOCK_STREAM, 1, NULL, 0)) { printf("have listening socket\n"); connection_socket = n; break; } } if (n == (SD_LISTEN_FDS_START + sd_listen_fds(0))) { perror("Not passed fd for local socket"); exit(EXIT_FAILURE); } printf("wait connection\n"); for (;;) { /* Wait for incoming connection. */ data_socket = accept(connection_socket, NULL, NULL); if (data_socket == -1) { perror("accept"); exit(EXIT_FAILURE); } printf("have connection\n"); connection(data_socket, file_socket); } close(connection_socket); exit(EXIT_SUCCESS); }