From: Ian Jackson Date: Wed, 22 Oct 2008 17:38:26 +0000 (+0100) Subject: Battery Management X-Git-Tag: t.master-before-merge~67^2~2 X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=3adfe9fff5034d9e4042d23e0d3887d1b0216c84;p=qemu-xen-4.2-testing.git Battery Management qemu side change for battery support within HVM guest. (as revised by Kamala `rev1' Tue, 21 Oct 2008 19:15:41 GMT) Signed-off-by: Kamala Narasimhan --- diff --git a/hw/battery_mgmt.c b/hw/battery_mgmt.c new file mode 100644 index 000000000..38b565845 --- /dev/null +++ b/hw/battery_mgmt.c @@ -0,0 +1,297 @@ +/* + * battery_mgmt.c + * + * Copyright (c) 2008 Kamala Narasimhan + * Copyright (c) 2008 Citrix Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Battery Management Implementation. Implements the following - + * 1) Check xenstore for pm implementation type - Pass-through or non-passthrough + * 2) Map appropriate ports for pass-through battery information + * 3) Register for appropriate ports and respond to those port read/writes for + * non-passthrough implementation + */ + +#include "hw.h" +#include "pc.h" +#include "qemu-xen.h" +#include "isa.h" //register_ioport_read declaration +#include "battery_mgmt.h" + +#include + +//#define BATTERY_MGMT_DEBUG +//#define BATTERY_MGMT_DEBUG_EXT + +#define BATTERY_PORT_1 0xb2 +#define BATTERY_PORT_2 0x86 +#define BATTERY_PORT_3 0x88 + +#define BATTERY_OP_INIT 0x7b +#define BATTERY_OP_SET_INFO_TYPE 0x7c /*BIF or BST */ +#define BATTERY_OP_GET_DATA_LENGTH 0x79 +#define BATTERY_OP_GET_DATA 0x7d + +#ifdef BATTERY_MGMT_DEBUG_EXT + #define BATTERY_DBG_PORT_1 0xB040 + #define BATTERY_DBG_PORT_2 0xB044 + #define BATTERY_DBG_PORT_3 0xB046 + #define BATTERY_DBG_PORT_4 0xB048 + static int monitor_battery_port = 0; +#endif + +static enum POWER_MGMT_MODE power_mgmt_mode = PM_MODE_NONE; +static battery_state_info battery_info; +extern FILE *logfile; +extern int domid; + +int is_battery_pt_feasible(void) +{ + int val; + + if ( (ioperm(BATTERY_PORT_1, 1, 1) != 0) || + (ioperm(BATTERY_PORT_2, 1, 1) != 0) ) + return 0; + + outb(BATTERY_OP_INIT, BATTERY_PORT_1); + outb(1 /*BIF*/, BATTERY_PORT_2); + outb(BATTERY_OP_SET_INFO_TYPE, BATTERY_PORT_1); + outb(0, BATTERY_PORT_2); + outb(BATTERY_OP_GET_DATA_LENGTH, BATTERY_PORT_1); + val = inb(BATTERY_PORT_2); + + ioperm(BATTERY_PORT_1, 1, 0); + ioperm(BATTERY_PORT_2, 1, 0); + if ( !val ) + fprintf(logfile, "xen_extended_power_mgmt set to 1 but this mode is incomapatible with the current firmware!\n"); + return val; +} + +void battery_mgmt_pt_mode_init(void) +{ + int xc; + + xc = xc_interface_open(); + if ( xc == -1 ) + { + fprintf(logfile, "%s: xc_interface_open failed\n", __FUNCTION__); + return; + } + + if ( xc_domain_ioport_mapping(xc, domid, BATTERY_PORT_1, BATTERY_PORT_1, 0x2, 1) != 0 ) + fprintf(logfile, "Failed to map port %x to guest\n", BATTERY_PORT_1); + + if ( xc_domain_ioport_mapping(xc, domid, BATTERY_PORT_2, BATTERY_PORT_2, 0x1, 1) != 0 ) + fprintf(logfile, "Failed to map port %x to guest\n", BATTERY_PORT_2); + + close(xc); +} + +#ifdef BATTERY_MGMT_DEBUG_EXT + +static void battery_dbg_monitor(void *opaque, uint32_t addr, uint32_t val) +{ + monitor_battery_port = !monitor_battery_port; +} + +static void battery_dbg_port_b2_input(void *opaque, uint32_t addr, uint32_t val) +{ + if ( monitor_battery_port ) + fprintf(logfile, "Input value to battery port 0xb2 - %d; port 0x86 - ", val); +} + +static void battery_dbg_port_86_output(void *opaque, uint32_t addr, uint32_t val) +{ + if ( monitor_battery_port ) + fprintf(logfile, "Output value from battery port 0x86 - %d\n", val); +} + +static void battery_dbg_port_86_input(void *opaque, uint32_t addr, uint32_t val) +{ + if ( monitor_battery_port ) + fprintf(logfile, "%d. ", val); +} +#endif //BATTERY_MGMT_DEBUG_EXT + +void get_battery_data_from_xenstore(void) +{ + battery_info.battery_data = 0; + battery_info.current_index = 0; + + if ( battery_info.type == BIF ) + battery_info.battery_data = xenstore_read_battery_data(0/*battery info*/); + else if ( battery_info.type == BST ) + battery_info.battery_data = xenstore_read_battery_data(1/*battery status*/); +} + +void write_battery_data_to_port(void) +{ + char temp[3]; + char *data; + + if ( battery_info.battery_data == NULL ) + return; + + data = battery_info.battery_data + battery_info.current_index; + //KN: @Todo - Revisit the hard coding below and add bounds checking + // for current index though the querying software is not likely to + // ask for more than what we provide as initial data length. + if ( ( battery_info.current_index <= 74 ) || + (( battery_info.current_index > 74 ) && (*(data - 1) == '\n' )) ) + { + snprintf(temp, 3, "%s", data); + battery_info.port_86_val = (uint8_t)strtoull(temp, NULL, 16); + battery_info.current_index+=2; + } else + { + if ( *data == '\n' ) + battery_info.port_86_val = 0; + else + battery_info.port_86_val = *data; + battery_info.current_index+=1; + } + +#ifdef BATTERY_MGMT_DEBUG_EXT + fprintf(logfile, "Wrote %d to port 0x86\n", battery_info.port_86_val); +#endif + return; +} + +static void battery_port_1_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + switch (val) + { + case BATTERY_OP_GET_DATA_LENGTH: + get_battery_data_from_xenstore(); + write_battery_data_to_port(); + battery_info.port_b2_val = 0; + break; + case BATTERY_OP_INIT: + battery_info.type = BATT_NONE; + if ( battery_info.battery_data != 0 ) + free(battery_info.battery_data); + battery_info.battery_data = 0; + battery_info.current_index = 0; + battery_info.port_b2_val = 0; + break; + case BATTERY_OP_SET_INFO_TYPE: + if ( battery_info.type == BATT_NONE ) + { + if ( battery_info.port_86_val == 1 ) + battery_info.type = BIF; + else if ( battery_info.port_86_val == 2 ) + { + xenstore_refresh_battery_status(); + battery_info.type = BST; + } + } + battery_info.port_b2_val = 0; + break; + case BATTERY_OP_GET_DATA: + write_battery_data_to_port(); + battery_info.port_b2_val = 0; + break; + default: + break; + } + return; +} + +static uint32_t battery_port_1_readb(void *opaque, uint32_t addr) +{ + return battery_info.port_b2_val; +} + +static void battery_port_2_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + battery_info.port_86_val = val; +} + +static uint32_t battery_port_2_readb(void *opaque, uint32_t addr) +{ + return battery_info.port_86_val; +} + +static uint32_t battery_port_3_readb(void *opaque, uint32_t addr) +{ + if ( power_mgmt_mode == PM_MODE_PT || power_mgmt_mode == PM_MODE_NON_PT ) { + if ( (power_mgmt_mode == PM_MODE_PT) && (is_battery_pt_feasible() == 0) ) + return 0x0F; + return 0x1F; + } + + return 0x0F; +} + + +void battery_mgmt_non_pt_mode_init(PCIDevice *device) +{ + memset(&battery_info, 0, sizeof(battery_state_info)); + register_ioport_read(BATTERY_PORT_1, 2, 1, battery_port_1_readb, device); + register_ioport_write(BATTERY_PORT_1, 2, 1, battery_port_1_writeb, device); + register_ioport_read(BATTERY_PORT_2, 1, 1, battery_port_2_readb, device); + register_ioport_write(BATTERY_PORT_2, 1, 1, battery_port_2_writeb, device); + +#ifdef BATTERY_MGMT_DEBUG_EXT + register_ioport_write(BATTERY_DBG_PORT_1, 1, 1, battery_dbg_monitor , device); + register_ioport_write(BATTERY_DBG_PORT_2, 1, 1, battery_dbg_port_b2_input, device); + register_ioport_write(BATTERY_DBG_PORT_3, 1, 1, battery_dbg_port_86_output, device); + register_ioport_write(BATTERY_DBG_PORT_4, 1, 1, battery_dbg_port_86_input, device); +#endif +} + +void battery_mgmt_init(PCIDevice *device) +{ + char *mode_buffer = NULL; + + //xen_extended_power_mgmt xenstore entry indicates whether or not extended power + //management support is requested for the hvm guest. Extended power management + //support includes power management support beyond S3, S4, S5. A value of 1 + //indicates pass-through pm support where upon pm resources are mapped to the guest + //as appropriate where as a value of 2 as set in non pass-through mode, requires qemu + //to take the onus of responding to relevant pm port reads/writes. + mode_buffer = xenstore_device_model_read(domid, "xen_extended_power_mgmt", NULL); + if ( mode_buffer == NULL ) + { +#ifdef BATTERY_MGMT_DEBUG + fprintf(logfile,"Xenpm mode not set\n"); +#endif + return; + } + + power_mgmt_mode = (enum POWER_MGMT_MODE) strtoull(mode_buffer, NULL, 10); + free(mode_buffer); + switch ( power_mgmt_mode ) + { + case PM_MODE_PT: + battery_mgmt_pt_mode_init(); + break; + case PM_MODE_NON_PT: + battery_mgmt_non_pt_mode_init(device); + break; + case PM_MODE_NONE: + default: + return; + } + + register_ioport_read(BATTERY_PORT_3, 1, 1, battery_port_3_readb, device); + +#ifdef BATTERY_MGMT_DEBUG + fprintf(logfile, "Power management mode set to - %d\n", power_mgmt_mode); +#endif +} + diff --git a/hw/battery_mgmt.h b/hw/battery_mgmt.h new file mode 100644 index 000000000..9b9b427d4 --- /dev/null +++ b/hw/battery_mgmt.h @@ -0,0 +1,38 @@ +/* + * battery_mgmt.h + * + * Copyright (c) 2008 Kamala Narasimhan + * Copyright (c) 2008 Citrix Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _BATTERY_MGMT_H +#define _BATTERY_MGMT_H + +enum POWER_MGMT_MODE { PM_MODE_NONE = 0, PM_MODE_PT, PM_MODE_NON_PT }; +enum BATTERY_INFO_TYPE { BATT_NONE, BIF, BST }; +typedef struct battery_state_info { + enum BATTERY_INFO_TYPE type; + uint8_t port_b2_val; + uint8_t port_86_val; + uint8_t port_66_val; + char *battery_data; + uint8_t current_index; +} battery_state_info; + +void battery_mgmt_init(PCIDevice *device); + +#endif diff --git a/hw/piix4acpi.c b/hw/piix4acpi.c index 74d41de9e..39d564589 100644 --- a/hw/piix4acpi.c +++ b/hw/piix4acpi.c @@ -28,6 +28,7 @@ #include "pci.h" #include "sysemu.h" #include "qemu-xen.h" +#include "battery_mgmt.h" #include #include @@ -188,6 +189,8 @@ static void acpi_map(PCIDevice *pci_dev, int region_num, /* Word access */ register_ioport_write(addr + 4, 2, 2, acpiPm1Control_writew, d); register_ioport_read(addr + 4, 2, 2, acpiPm1Control_readw, d); + + battery_mgmt_init(pci_dev); } #ifdef CONFIG_PASSTHROUGH diff --git a/qemu-xen.h b/qemu-xen.h index b7ace3d29..8704774b2 100644 --- a/qemu-xen.h +++ b/qemu-xen.h @@ -85,6 +85,9 @@ int xenstore_unsubscribe_from_hotplug_status(struct xs_handle *handle, int xenstore_vm_write(int domid, char *key, char *val); char *xenstore_vm_read(int domid, char *key, unsigned int *len); +char *xenstore_device_model_read(int domid, char *key, unsigned int *len); +char *xenstore_read_battery_data(int battery_status); +int xenstore_refresh_battery_status(); /* xenfbfront.c */ int xenfb_pv_display_init(DisplayState *ds); diff --git a/xen-hooks.mak b/xen-hooks.mak index 230d03ac5..e87455285 100644 --- a/xen-hooks.mak +++ b/xen-hooks.mak @@ -32,6 +32,7 @@ OBJS += xen_console.o OBJS += xen_machine_fv.o OBJS += exec-dm.o OBJS += pci_emulation.o +OBJS += battery_mgmt.o ifdef CONFIG_STUBDOM CPPFLAGS += $(TARGET_CPPFLAGS) diff --git a/xenstore.c b/xenstore.c index 5cd50638c..3b3883765 100644 --- a/xenstore.c +++ b/xenstore.c @@ -1154,6 +1154,86 @@ int xenstore_vm_write(int domid, char *key, char *value) return rc; } +char *xenstore_device_model_read(int domid, char *key, unsigned int *len) +{ + char *path = NULL, *value = NULL; + + if (pasprintf(&path, "/local/domain/0/device-model/%d/%s", domid, key) == -1) + return NULL; + + value = xs_read(xsh, XBT_NULL, path, len); + if (value == NULL) + fprintf(logfile, "xs_read(%s): read error\n", path); + + free(path); + return value; +} + +char *xenstore_extended_power_mgmt_read(char *key, unsigned int *len) +{ + char *path = NULL, *value = NULL; + + if (pasprintf(&path, "/pm/%s", key) == -1) + return NULL; + + value = xs_read(xsh, XBT_NULL, path, len); + if (value == NULL) + fprintf(logfile, "xs_read(%s): read error\n", path); + + free(path); + return value; +} + +int xenstore_extended_power_mgmt_write(char * key, char * value) +{ + int ret; + char *path = NULL; + + if (pasprintf(&path, "/pm/%s", key) == -1) + return NULL; + + ret = xs_write(xsh, XBT_NULL, path, value, strlen(value)); + free(path); + return ret; +} + +int xenstore_extended_power_mgmt_event_trigger(char *key, char * value) +{ + int ret; + char *path = NULL; + + if (pasprintf(&path, "events/%s", key) == -1) + return NULL; + + ret = xenstore_extended_power_mgmt_write(path, value); + free(path); + return ret; +} + +/* + * Xen power management daemon stores battery generic information + * like model, make, design volt, capacity etc. under /pm/bif and + * battery status information like charging/discharging rate + * under /pm/bst in xenstore. + */ +char *xenstore_read_battery_data(int battery_status) +{ + if ( battery_status == 1 ) + return xenstore_extended_power_mgmt_read("bst", NULL); + else + return xenstore_extended_power_mgmt_read("bif", NULL); +} + +/* + * We set /pm/events/refreshbatterystatus xenstore entry + * to refresh battert status info stored under /pm/bst + * Xen power management daemon watches for changes to this + * entry and triggers a refresh. + */ +int xenstore_refresh_battery_status() +{ + return xenstore_extended_power_mgmt_event_trigger("refreshbatterystatus", "1"); +} /* * Create a store entry for a device (e.g., monitor, serial/parallel lines).