blob: e3250ddb08eb880ae55f18c50723e7b194393e40 [file] [log] [blame] [raw]
/*
htop - vmkernel/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
Copyright 2015-2026 Rivoreo
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
/*{
#include "Action.h"
#include "BatteryMeter.h"
#include "SignalsPanel.h"
#include "VMKernelProcess.h"
#include <stdint.h>
#include <stdlib.h>
#define PLATFORM_SUPPORT_PROCESS_O_STATE
#define PLATFORM_PRESENT_THREADS_AS_PROCESSES
#define VMKERNEL_VERSION_5_5 55
#define VMKERNEL_VERSION_6_0 60
#define VMKERNEL_VERSION_6_5 65
#define VMKERNEL_VERSION_6_7 67
#define SYS_VSI_Get 1194
#define SYS_VSI_GetList 1197
#define SYS_VSI_GetNodeInfo 1202
#define SYS_GetMemSize 1215
#define SYS_GetNumCPUsUsed 1221
#define SYS_GetUptimeUS 1216
struct vsi_node_info {
uint32_t id;
uint8_t _unknown_1[9];
uint32_t parent_id;
uint32_t sibling_id;
uint32_t first_child_id;
uint8_t _unknown_2[28];
uint64_t checksum;
uint8_t _unknown_3[67];
} __attribute__((__packed__));
struct vsi_param {
uint32_t type;
uint64_t value;
uint8_t _reserved[28];
} __attribute__((__packed__));
struct vsi_param_6_7 {
uint64_t value;
uint8_t _reserved[20];
uint32_t type;
} __attribute__((__packed__));
struct vsi_list {
uint32_t type_or_version;
uint32_t flags;
uint32_t used_count;
uint32_t instance_count;
uint32_t allocated_count;
uint32_t param_size;
uint32_t unknown_1;
uint32_t string_size;
uint64_t string_offset;
uint64_t self_ptr;
uint64_t unknown_2[2];
union {
struct vsi_param v5[0];
struct vsi_param_6_7 v6_7[0];
} param;
};
static inline uint64_t Platform_vsiListGetValue(const struct vsi_list *list, int i) {
extern int Platform_running_vmkernel_version;
switch(Platform_running_vmkernel_version) {
case VMKERNEL_VERSION_5_5:
case VMKERNEL_VERSION_6_0:
case VMKERNEL_VERSION_6_5:
return list->param.v5[i].value;
case VMKERNEL_VERSION_6_7:
return list->param.v6_7[i].value;
default:
abort();
}
}
}*/
#include "Platform.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "UptimeMeter.h"
#include "UsersMeter.h"
#include "CRT.h"
#include "vmk_error_codes.h"
#include <sys/utsname.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#ifdef __i386__
#define VMKSC_0(N) ({ int e; __asm__ __volatile__("int $0x90" : "=a"(e) : "a"(N) : "memory"); e; })
#define VMKSC_1(N,A1) \
({ int e; __asm__ __volatile__("int $0x90" : "=a"(e) : "a"(N), "c"(A1) : "memory"); e; })
#define VMKSC_2(N,A1,A2) \
({ int e; __asm__ __volatile__("int $0x90" : "=a"(e) : "a"(N), "c"(A1), "d"(A2) : "memory"); e; })
#define VMKSC_3(N,A1,A2,A3) \
({ int e; __asm__ __volatile__("int $0x90" : "=a"(e) : "a"(N), "c"(A1), "d"(A2), "S"(A3) : "memory"); e; })
#define VMKSC_4(N,A1,A2,A3,A4) \
({ int e; __asm__ __volatile__("int $0x90" : "=a"(e) : "a"(N), "c"(A1), "d"(A2), "S"(A3), "D"(A4) : "memory"); e; })
#define VMKSC_VA_6(A1,A2,A3,A4,A5,A6,...) A6
#define VMKSC_VA(...) VMKSC_VA_6(__VA_ARGS__,VMKSC_4,VMKSC_3,VMKSC_2,VMKSC_1,VMKSC_0,)
#define VMKSC(...) VMKSC_VA(__VA_ARGS__)(__VA_ARGS__)
#else
#define VMKSC syscall
#endif
const SignalItem Platform_signals[] = {
{ .name = "Cancel", .number = 0 },
#define SIG(NAME) { .name = #NAME, .number = SIG##NAME }
SIG(HUP),
SIG(INT),
SIG(QUIT),
SIG(ILL),
SIG(TRAP),
SIG(ABRT),
#ifdef SIGIOT
SIG(IOT),
#endif
#ifdef SIGEMT
SIG(EMT),
#endif
SIG(BUS),
SIG(FPE),
SIG(KILL),
SIG(USR1),
SIG(SEGV),
SIG(USR2),
SIG(PIPE),
SIG(ALRM),
SIG(TERM),
#ifdef SIGSTKFLT
SIG(STKFLT),
#endif
SIG(CHLD),
SIG(CONT),
SIG(STOP),
SIG(TSTP),
SIG(TTIN),
SIG(TTOU),
SIG(URG),
SIG(XCPU),
SIG(XFSZ),
SIG(VTALRM),
SIG(PROF),
SIG(WINCH),
SIG(IO),
#ifdef SIGPOLL
SIG(POLL),
#endif
#ifdef SIGPWR
SIG(PWR),
#endif
#ifdef SIGINFO
SIG(INFO),
#endif
#ifdef SIGLOST
SIG(LOST),
#endif
#ifdef SIGSYS
SIG(SYS),
#endif
#undef SIG
};
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
ProcessField Platform_defaultFields[] = {
HTOP_PID_FIELD, HTOP_EFFECTIVE_USER_FIELD, HTOP_NICE_FIELD, HTOP_M_SIZE_FIELD, HTOP_M_RESIDENT_FIELD, HTOP_STATE_FIELD, HTOP_PERCENT_CPU_FIELD, HTOP_PERCENT_MEM_FIELD, HTOP_TIME_FIELD, HTOP_COMM_FIELD, 0
};
const unsigned int Platform_numberOfFields = HTOP_LAST_PROCESSFIELD;
MeterClass* Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
&SwapMeter_class,
&TasksMeter_class,
#ifdef HAVE_UTMPX
&UsersMeter_class,
#endif
&BatteryMeter_class,
&HostnameMeter_class,
&UptimeMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
&BlankMeter_class,
NULL
};
void Platform_setBindings(Htop_Action* keys) {
(void) keys;
}
int Platform_getUptime() {
uint64_t uptime;
int e = VMKSC(SYS_GetUptimeUS, &uptime);
return e == VMK_OK ? (int)(uptime / 1000000) : -1;
}
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
double values[3] = { 0 };
getloadavg(values, 3);
*one = values[0];
*five = values[1];
*fifteen = values[2];
}
int Platform_getMaxPid() {
return (1 << 30) - 1;
}
double Platform_updateCPUValues(Meter *meter, int cpu) {
// TODO
return 0;
}
void Platform_updateMemoryValues(Meter *meter) {
#if 0
uint64_t total, free;
unsigned int e = VMKSC(SYS_GetMemSize, &total, &free);
if(e != VMK_OK) return;
meter->total = total / 1024;
meter->values[0] = (total - free) / 1024;
#else
const ProcessList *pl = meter->pl;
meter->total = pl->totalMem;
meter->values[0] = pl->usedMem;
#endif
}
void Platform_updateSwapValues(Meter *meter) {
// TODO
}
char **Platform_getProcessArgv(const Process *proc) {
return NULL;
}
char **Platform_getProcessEnvv(const Process *proc) {
return NULL;
}
bool Platform_haveSwap() {
// TODO
return false;
}
int Platform_vsiGetNodeIdAndChecksum(uint32_t parent_node_id, const char *node_name, uint32_t *node_id, uint64_t *node_cksum) {
struct vsi_node_info info_buffer;
char name_buffer[128];
#ifdef __i386__
uint32_t extra_args[2] = { (uint32_t)name_buffer, sizeof info_buffer };
int e = VMKSC(SYS_VSI_GetNodeInfo, parent_node_id, 0, &info_buffer, extra_args);
#else
int e = syscall(SYS_VSI_GetNodeInfo, parent_node_id, 0,
&info_buffer, name_buffer, sizeof info_buffer);
#endif
if(e) return e;
uint32_t child_node_id = info_buffer.first_child_id;
do {
#ifdef __i386__
e = VMKSC(SYS_VSI_GetNodeInfo, child_node_id, 0, &info_buffer, extra_args);
#else
e = syscall(SYS_VSI_GetNodeInfo, child_node_id, 0,
&info_buffer, name_buffer, sizeof info_buffer);
#endif
if(e) return e;
if(strcmp(name_buffer, node_name) == 0) {
if(node_id) *node_id = info_buffer.id;
if(node_cksum) memcpy(node_cksum, &info_buffer.checksum, 8);
return VMK_OK;
}
child_node_id = info_buffer.sibling_id;
} while(child_node_id && child_node_id != UINT32_MAX);
//return VMK_NOT_FOUND;
return ENOENT;
}
int Platform_running_vmkernel_version;
int Platform_vsi_int;
int Platform_vsi_string;
#ifndef __i386__
static int vsi_get_5_0(uint32_t node_id, uint64_t node_cksum, const struct vsi_list *list, void *buffer, size_t size) {
struct {
uint64_t checksum;
void *buffer;
size_t buffer_size;
uint64_t _unknown_1;
} extra_args[2] = {
{ node_cksum, buffer, size },
{ node_cksum, buffer, size }
};
return syscall(SYS_VSI_Get, node_id, 0, list, list->param_size, 0, extra_args);
}
static int vsi_get_6_5(uint32_t node_id, uint64_t node_cksum, const struct vsi_list *list, void *buffer, size_t size) {
struct {
uint32_t _unknown_1;
void *buffer;
size_t buffer_size;
} __attribute__((__packed__)) extra_args = {
0, buffer, size
};
return syscall(SYS_VSI_Get, node_id, 0, node_cksum, list, list->param_size, &extra_args);
}
static int (*vsi_get)(uint32_t, uint64_t, const struct vsi_list *, void *, size_t);
static int vsi_get_list_5_0(uint32_t node_id, uint64_t node_cksum, struct vsi_list *list, size_t size) {
struct vsi_list input_list = {
.type_or_version = 1, .param_size = sizeof(struct vsi_list), .self_ptr = (uintptr_t)&input_list
};
struct {
uint64_t checksum;
size_t output_size;
} extra_args[2] = {
{ node_cksum, size }, { node_cksum, size }
};
return syscall(SYS_VSI_GetList, node_id, 0, &input_list, input_list.param_size, list,
extra_args);
}
static int vsi_get_list_6_5(uint32_t node_id, uint64_t node_cksum, struct vsi_list *list, size_t size) {
struct vsi_list input_list = {
.type_or_version = 1, .param_size = sizeof(struct vsi_list), .self_ptr = (uintptr_t)&input_list
};
struct {
struct vsi_list *output_list;
size_t output_size;
} extra_args[2] = {
{ list, size }, { list, size }
};
return syscall(SYS_VSI_GetList, node_id, 0, node_cksum, &input_list, input_list.param_size,
extra_args);
}
static int (*vsi_get_list)(uint32_t, uint64_t, struct vsi_list *, size_t);
#endif
void Platform_checkVMkernelVersion() {
struct utsname utsname;
if(uname(&utsname) < 0) CRT_fatalError("uname", 0);
if(strcmp(utsname.sysname, "VMkernel")) CRT_fatalError("Kernel type not supported", EPERM);
char *end_p;
long int major_version = strtol(utsname.release, &end_p, 10);
if(end_p == utsname.release || *end_p != '.') {
CRT_fatalError("Kernel version not supported", EPERM);
}
const char *begin_p = end_p + 1;
long int minor_version = strtol(begin_p, &end_p, 10);
if(end_p == begin_p || *end_p != '.') CRT_fatalError("Kernel version not supported", EPERM);
Platform_running_vmkernel_version = major_version * 10 + minor_version;
switch(Platform_running_vmkernel_version) {
case VMKERNEL_VERSION_5_5:
Platform_vsi_int = 0;
Platform_vsi_string = 1;
#ifndef __i386__
vsi_get = vsi_get_5_0;
vsi_get_list = vsi_get_list_5_0;
#endif
break;
case VMKERNEL_VERSION_6_0:
Platform_vsi_int = 0;
Platform_vsi_string = 1;
#ifndef __i386__
vsi_get = vsi_get_5_0;
vsi_get_list = vsi_get_list_5_0;
#endif
break;
case VMKERNEL_VERSION_6_5:
Platform_vsi_int = 0;
Platform_vsi_string = 1;
#ifndef __i386__
vsi_get = vsi_get_6_5;
vsi_get_list = vsi_get_list_6_5;
#endif
break;
case VMKERNEL_VERSION_6_7:
Platform_vsi_int = 1;
Platform_vsi_string = 2;
#ifndef __i386__
vsi_get = vsi_get_6_5;
vsi_get_list = vsi_get_list_6_5;
#endif
break;
default:
CRT_fatalError("Kernel version not supported", EPERM);
}
}
void Platform_vsiListAddInt(struct vsi_list *list, uint64_t value) {
uint32_t i = list->used_count;
assert(i < list->allocated_count);
switch(Platform_running_vmkernel_version) {
case VMKERNEL_VERSION_5_5:
case VMKERNEL_VERSION_6_0:
case VMKERNEL_VERSION_6_5:
list->param.v5[i].type = 0;
list->param.v5[i].value = value;
break;
case VMKERNEL_VERSION_6_7:
list->param.v6_7[i].type = 1;
list->param.v6_7[i].value = value;
break;
default:
abort();
}
list->used_count++;
list->instance_count++;
}
int Platform_vsiGet(uint32_t node_id, uint64_t node_cksum, const struct vsi_list *list, void *buffer, size_t size) {
#ifdef __i386__
struct {
uint64_t checksum;
size_t input_size;
uint32_t _unknown_1;
void *buffer;
size_t buffer_size;
} extra_args[2] = {
{ node_cksum, list->param_size, 0, buffer, size },
{ node_cksum, list->param_size, 0, buffer, size }
};
return VMKSC(SYS_VSI_Get, node_id, 0, list, extra_args);
#else
if(!vsi_get) Platform_checkVMkernelVersion();
return vsi_get(node_id, node_cksum, list, buffer, size);
#endif
}
int Platform_vsiGetList(uint32_t node_id, uint64_t node_cksum, struct vsi_list *list, size_t size) {
#ifdef __i386__
struct vsi_list input_list = {
.type_or_version = 1, .param_size = sizeof(struct vsi_list), .self_ptr = (uintptr_t)&input_list
};
struct {
uint64_t checksum;
size_t input_size;
struct vsi_list *output_list;
size_t output_size;
} extra_args[2] = {
{ node_cksum, input_list.param_size, list, size },
{ node_cksum, input_list.param_size, list, size }
};
return VMKSC(SYS_VSI_GetList, node_id, 0, &input_list, extra_args);
#else
if(!vsi_get_list) Platform_checkVMkernelVersion();
return vsi_get_list(node_id, node_cksum, list, size);
#endif
}