Compartir a través de


Interfaz de Hypercall

El hipervisor proporciona un mecanismo de llamada para invitados. Estas llamadas se conocen como hiperllamadas. Cada hiperllamada define un conjunto de parámetros de entrada o salida. Estos parámetros se especifican en términos de una estructura de datos basada en memoria. Todos los elementos de las estructuras de datos de entrada y salida se rellenan en límites naturales de hasta 8 bytes (es decir, los elementos de dos bytes deben estar en límites de dos bytes, etc.).

Opcionalmente, se puede usar una segunda convención de llamada de hiperllamada para un subconjunto de hiperllamadas, en particular, aquellas que tienen dos o menos parámetros de entrada y ningún parámetro de salida. Al usar esta convención de llamada, los parámetros de entrada se pasan en registros de uso general.

Opcionalmente, se puede usar una tercera convención de llamada de hiperllamada para un subconjunto de hiperllamadas donde el bloque de parámetros de entrada es de hasta 112 bytes. Al usar esta convención de llamada, los parámetros de entrada se pasan en registros, incluidos los registros XMM volátiles.

Las estructuras de datos de entrada y salida deben colocarse en memoria en un límite de 8 bytes y rellenarse en un múltiplo de 8 bytes de tamaño. El hipervisor omite los valores de las regiones de relleno.

En el caso de la salida, el hipervisor puede sobrescribir (pero no se garantiza) las regiones de relleno. Si sobrescribe las regiones de relleno, escribirá ceros.

Clases de Hypercall

Hay dos clases de hiperllamadas: simple y rep (breve para "repetir"). Una hiperllamada simple realiza una sola operación y tiene un conjunto de tamaño fijo de parámetros de entrada y salida. Una hiperllamada de rep actúa como una serie de hiperllamadas simples. Además de un conjunto de tamaño fijo de parámetros de entrada y salida, las hiperllamadas de repetición implican una lista de elementos de entrada o salida de tamaño fijo.

Cuando un autor de llamada invoca inicialmente una hiperllamada de rep, especifica un recuento de rep que indica el número de elementos de la lista de parámetros de entrada o salida. Los autores de llamadas también especifican un índice de inicio de rep que indica el siguiente elemento de entrada o salida que se debe consumir. El hipervisor procesa los parámetros rep en orden de lista, es decir, aumentando el índice de elementos.

En el caso de las invocaciones posteriores de la hiperllamada de rep, el índice de inicio de la reproducción indica cuántos elementos se han completado y, junto con el valor de recuento de rep, cuántos elementos quedan. Por ejemplo, si un autor de la llamada especifica un recuento de repeticiones de 25 y solo se completan 20 iteraciones dentro de las restricciones de tiempo, la hiperllamada devuelve el control al procesador virtual que realiza la llamada después de actualizar el índice de inicio de la representación a 20. Cuando se vuelve a ejecutar la hiperllamada, el hipervisor se reanudará en el elemento 20 y completará los 5 elementos restantes.

Si se produce un error al procesar un elemento, se proporciona un código de estado adecuado junto con un recuento completado de reps, que indica el número de elementos que se procesaron correctamente antes de que se encontrara el error. Suponiendo que la palabra de control de hiperllamada especificada es válida (vea lo siguiente) y se puede acceder a las listas de parámetros de entrada y salida, se garantiza que el hipervisor intente al menos un rep, pero no es necesario procesar toda la lista antes de devolver el control al autor de la llamada.

Continuación de Hypercall

Una hiperllamada se puede considerar como una instrucción compleja que toma muchos ciclos. El hipervisor intenta limitar la ejecución de hiperllamadas a 50μs o menos antes de devolver el control al procesador virtual que invocó la hiperllamada. Algunas operaciones de hiperllamada son lo suficientemente complejas que una garantía de 50μs es difícil de hacer. Por lo tanto, el hipervisor se basa en un mecanismo de continuación de hiperllamada para algunas hiperllamadas, incluidos todos los formularios de hiperllamada de rep.

El mecanismo de continuación de hiperllamada es principalmente transparente para el autor de la llamada. Si una hiperllamada no puede completarse dentro del límite de tiempo prescrito, el control se devuelve al autor de la llamada, pero el puntero de instrucción no está avanzado más allá de la instrucción que invocó la hiperllamada. Esto permite controlar las interrupciones pendientes y programar otros procesadores virtuales. Cuando el subproceso de llamada original reanuda la ejecución, volverá a ejecutar la instrucción hypercall y avanzará hacia la finalización de la operación.

Se garantiza que la mayoría de las hiperllamadas simples se completen dentro del límite de tiempo prescrito. Sin embargo, un pequeño número de hiperllamadas simples podría requerir más tiempo. Estas hiperllamadas usan la continuación de hypercall de forma similar a las hiperllamadas de rep. En tales casos, la operación implica dos o más estados internos. La primera invocación coloca el objeto (por ejemplo, la partición o el procesador virtual) en un estado y, después de las invocaciones repetidas, el estado finalmente pasa a un estado terminal. Para cada hiperllamada que sigue este patrón, se describen los efectos secundarios visibles de los estados internos intermedios.

Atomicidad y ordenación de hiperllamadas

Excepto cuando se indique, la acción realizada por una hiperllamada es atómica tanto con respecto a todas las demás operaciones invitadas (por ejemplo, instrucciones ejecutadas dentro de un invitado) como todas las demás hiperllamadas que se ejecutan en el sistema. Una simple hiperllamada realiza una sola acción atómica; Una hiperllamada de rep realiza varias acciones atómicas independientes.

Las hiperllamadas simples que usan la continuación de hypercall pueden implicar varios estados internos que son visibles externamente. Estas llamadas componen varias operaciones atómicas.

Cada acción de hiperllamada puede leer parámetros de entrada o escribir resultados. Las entradas de cada acción se pueden leer en cualquier granularidad y en cualquier momento después de realizar la hiperllamada y antes de que se ejecute la acción. Los resultados (es decir, los parámetros de salida) asociados a cada acción se pueden escribir en cualquier granularidad y en cualquier momento después de ejecutar la acción y antes de que se devuelva la hiperllamada.

El invitado debe evitar el examen o manipulación de cualquier parámetro de entrada o salida relacionado con una hiperllamada en ejecución. Aunque un procesador virtual que ejecuta una hiperllamada no podrá hacerlo (ya que su ejecución de invitado se suspende hasta que se devuelve la hiperllamada), no hay nada para evitar que otros procesadores virtuales lo hagan. Los invitados que se comportan de esta manera pueden bloquearse o causar daños dentro de su partición.

Las hiperllamadas solo se pueden invocar desde el modo de procesador invitado con más privilegios. En plataformas x64, esto significa modo protegido con un nivel de privilegio actual (CPL) de cero. Aunque el código en modo real se ejecuta con una CPL eficaz de cero, no se permiten hiperllamadas en modo real. Un intento de invocar una hiperllamada dentro de un modo de procesador no válido generará una excepción de #UD (operación sin definir) en x64 y una excepción de instrucción no definida en ARM64.

Todas las hiperllamadas deben invocarse a través de la interfaz de hiperllamada definida por la arquitectura (consulte a continuación). Un intento de invocar una hiperllamada por cualquier otro medio (por ejemplo, copiar el código de la página de códigos de hypercall a una ubicación alternativa y ejecutarla desde allí) podría dar lugar a una excepción de operación no definida (#UD). No se garantiza que el hipervisor entregue esta excepción.

Requisitos de alineación

Los autores de llamadas deben especificar la dirección física de invitado (GPA) de 64 bits de los parámetros de entrada o salida. Los punteros GPA deben estar alineados con 8 bytes. Si la hiperllamada no implica parámetros de entrada o salida, el hipervisor omite el puntero GPA correspondiente.

Las listas de parámetros de entrada y salida no se pueden superponer ni cruzar los límites de página. Se espera que las páginas de entrada y salida de Hypercall sean páginas de GPA y no páginas de "superposición". Si el procesador virtual escribe los parámetros de entrada en una página de superposición y especifica un GPA dentro de esta página, el acceso del hipervisor a la lista de parámetros de entrada no está definido.

El hipervisor validará que la partición que realiza la llamada puede leer desde la página de entrada antes de ejecutar la hiperllamada solicitada. Esta validación consta de dos comprobaciones: el GPA especificado se asigna y el GPA se marca como legible. Si se produce un error en cualquiera de estas pruebas, el hipervisor genera un mensaje de interceptación de memoria. En el caso de las hiperllamadas que tienen parámetros de salida, el hipervisor validará que la partición puede escribir en la página de salida. Esta validación consta de dos comprobaciones: el GPA especificado se asigna y el GPA se marca como grabable.

Entradas de Hiperllamada

Los autores de llamadas especifican una hiperllamada por un valor de 64 bits denominado valor de entrada de hypercall. Tiene el formato siguiente:

Campo bits Información proporcionada
Código de llamada 15-0 Especifica qué hiperllamada se solicita.
Rápido 16 Especifica si la hiperllamada usa la convención de llamada basada en registros: 0 = basada en memoria, 1 = basada en registros.
Tamaño de encabezado variable 26-17 Tamaño de un encabezado de variable, en QWORDS.
RsvdZ 30-27 Debe ser cero
Está anidado 31 Especifica que el hipervisor L0 debe controlar la hiperllamada en un entorno anidado.
Recuento de representantes 43-32 Número total de representantes (para la llamada de representante, debe ser cero de lo contrario)
RsvdZ 47-44 Debe ser cero
Índice de inicio de la rep 59-48 Índice inicial (para la llamada de rep, debe ser cero de lo contrario)
RsvdZ 63-60 Debe ser cero

En el caso de las hiperllamadas de rep, el campo recuento de repeticiones indica el número total de reps. El índice de inicio del representante indica la repetición concreta en relación con el inicio de la lista (cero indica que se va a procesar el primer elemento de la lista). Por lo tanto, el valor de recuento de representantes siempre debe ser mayor que el índice de inicio del representante.

Convenciones de registro de Hypercall (x86/x64)

En x86/x64, registre la asignación de entradas de hiperllamada cuando la marca Fast es cero es la siguiente:

x64 x86 Información proporcionada
RCX EDX:EAX Valor de entrada de Hypercall
RDX EBX:ECX GPA de parámetros de entrada
R8 EDI:ESI Parámetros de salida GPA

El valor de entrada de hypercall se pasa en registros junto con un GPA que apunta a los parámetros de entrada y salida.

Las asignaciones de registro dependen de si el autor de la llamada se está ejecutando en modo de 32 bits (x86) o de 64 bits (x64). El hipervisor determina el modo del autor de la llamada en función del valor de EFER. LMA y CS.L. Si se establecen ambas marcas, se supone que el autor de la llamada es un llamador de 64 bits.

Registre la asignación de entradas de hiperllamada cuando la marca Fast sea una:

x64 x86 Información proporcionada
RCX EDX:EAX Valor de entrada de Hypercall
RDX EBX:ECX Parámetro de entrada
R8 EDI:ESI Parámetro de salida

El valor de entrada de hypercall se pasa en registros junto con los parámetros de entrada.

Convenciones de registro de Hypercall (ARM64 SMCCC)

En ARM64, las hiperllamadas se ejecutan mediante la instrucción "HVC #0". Las llamadas se adhieren a la convención de llamada SMCCC (SMCCC) de ARM64.

La asignación de registros para las entradas de hypercall es la siguiente:

Register Información proporcionada
X0 Identificador de función SMCCC
X1 Valor de entrada de Hypercall
X2 GPA de parámetros de entrada
X3 Parámetros de salida GPA

El identificador de función SMCCC en X0 sigue este formato:

bits Campo Importancia Description
31 Rendimiento de la llamada 0 Siempre 0
30 Convención de llamada 1 1 para las convenciones de llamada de HVC64
29:24 Tipo de llamada de servicio 6 6 para llamadas de servicio de hipervisor específicas del proveedor
23:16 Reservado 0 Reservado, debe ser cero (Res0)
15:0 Número de función 1 1 indica que el código de llamada de HV se define en X1

El formato completo del identificador de función: 0x46000001

Convenciones de registro de Hypercall (ARM64 HVC #1)

Por motivos históricos, la interfaz del hipervisor ARM64 también admite una convención de llamada diferente. Las hiperllamadas se ejecutan con la instrucción "HVC #1". Se recomienda usar la convención de llamada SMCCC para el nuevo código.

La asignación de registros para las entradas de hypercall es la siguiente:

Register Información proporcionada
X0 Valor de entrada de Hypercall
X1 GPA de parámetros de entrada
X2 Parámetros de salida GPA

Encabezados de entrada de Hiperllamada de tamaño variable

La mayoría de los encabezados de entrada de hiperllamada tienen un tamaño fijo. La cantidad de datos de encabezado que se pasan del invitado al hipervisor se especifica implícitamente mediante el código de hiperllamada y no se debe especificar por separado. Sin embargo, algunas hiperllamadas requieren una cantidad variable de datos de encabezado. Estas hiperllamadas suelen tener un encabezado de entrada de tamaño fijo y una entrada de encabezado adicional que sea de tamaño variable.

Un encabezado de tamaño variable es similar a una entrada de hiperllamada fija (alineada a 8 bytes y con un tamaño de múltiplo de 8 bytes). El autor de la llamada debe especificar la cantidad de datos que proporciona como encabezados de entrada. Este tamaño se proporciona como parte del valor de entrada de hiperllamada (vea "Tamaño de encabezado variable" en la tabla anterior).

Dado que el tamaño fijo del encabezado es implícito, en lugar de proporcionar el tamaño total del encabezado, solo se proporciona la parte variable en los controles de entrada:

Variable Header Bytes = {Total Header Bytes - sizeof(Fixed Header)} rounded up to nearest multiple of 8

Variable Header Size = Variable Header Bytes / 8

No es válido especificar un tamaño de encabezado de variable distinto de cero para una hiperllamada que no se documenta explícitamente como la aceptación de encabezados de entrada de tamaño variable. En tal caso, la hiperllamada dará como resultado un código de retorno de HV_STATUS_INVALID_HYPERCALL_INPUT.

Es posible que para una invocación determinada de una hiperllamada que acepte encabezados de entrada de tamaño variable que todas las entradas de encabezado se ajusten completamente dentro del encabezado de tamaño fijo. En tales casos, el encabezado de entrada de tamaño variable tiene un tamaño cero y los bits correspondientes de la entrada de hypercall deben establecerse en cero.

En todos los demás aspectos, las hiperllamadas que aceptan encabezados de entrada de tamaño variable son similares a las hiperllamadas de encabezado de entrada de tamaño fijo con respecto a las convenciones de llamada. También es posible que una hiperllamada de encabezado de tamaño variable admita además la semántica de rep. En tal caso, los elementos rep se encuentran después del encabezado de la manera habitual, excepto que el tamaño total del encabezado incluye las partes fijas y variables. Todas las demás reglas siguen siendo las mismas, por ejemplo, el primer elemento rep debe estar alineado con 8 bytes.

Entrada de Hiperllamada rápida XMM (x86/x64)

En las plataformas x86/x64, el hipervisor admite el uso de hiperllamadas rápidas XMM, lo que permite que algunas hiperllamadas aprovechen el rendimiento mejorado de la interfaz de hiperllamada rápida aunque requieran más de dos parámetros de entrada. La interfaz de hiperllamada rápida XMM usa seis registros XMM para permitir que el autor de la llamada pase un bloque de parámetros de entrada de hasta 112 bytes de tamaño.

La disponibilidad de la interfaz de hiperllamada rápida XMM se indica a través de la hoja de CPUID del hipervisor (0x40000003):

  • Bit 4: la compatibilidad para pasar la entrada de hiperllamada a través de registros XMM está disponible.

Tenga en cuenta que hay una marca independiente para indicar la compatibilidad con la salida rápida XMM. Cualquier intento de usar esta interfaz cuando el hipervisor no indica la disponibilidad producirá un error de #UD.

Registrar asignación (solo entrada)

x64 x86 Información proporcionada
RCX EDX:EAX Valor de entrada de Hypercall
RDX EBX:ECX Bloque de parámetros de entrada
R8 EDI:ESI Bloque de parámetros de entrada
XMM0 XMM0 Bloque de parámetros de entrada
XMM1 XMM1 Bloque de parámetros de entrada
XMM2 XMM2 Bloque de parámetros de entrada
XMM3 XMM3 Bloque de parámetros de entrada
XMM4 XMM4 Bloque de parámetros de entrada
XMM5 XMM5 Bloque de parámetros de entrada

El valor de entrada de hypercall se pasa en registros junto con los parámetros de entrada. Las asignaciones de registro dependen de si el autor de la llamada se está ejecutando en modo de 32 bits (x86) o de 64 bits (x64). El hipervisor determina el modo del autor de la llamada en función del valor de EFER. LMA y CS.L. Si se establecen ambas marcas, se supone que el autor de la llamada es un llamador de 64 bits. Si el bloque de parámetros de entrada es menor que 112 bytes, se omiten los bytes adicionales de los registros.

Registrar entrada de llamada rápida (ARM64 SMCCC)

En las plataformas ARM64, el hipervisor admite el uso de hiperllamadas rápidas de registro, lo que permite que algunas hiperllamadas aprovechen el rendimiento mejorado de la interfaz de hiperllamada rápida aunque requieran más de dos parámetros de entrada. La interfaz de hiperllamada rápida del registro usa 16 registros de uso general para permitir que el autor de la llamada pase un bloque de parámetros de entrada de hasta 128 bytes de tamaño.

Registrar asignación (solo entrada)

Register Información proporcionada
X0 Identificador de función SMCCC
X1 Valor de entrada de Hypercall
X2 - X17 Bloque de parámetros de entrada

Si el bloque de parámetros de entrada es menor que 128 bytes, se omiten los bytes adicionales de los registros.

Registrar entrada de llamada rápida (ARM64 HVC #1)

La interfaz de hiperllamada rápida del registro usa dieciséis registros de uso general para permitir que el autor de la llamada pase un bloque de parámetros de entrada de hasta 128 bytes de tamaño.

Registrar asignación (solo entrada)

Register Información proporcionada
X0 Valor de entrada de Hypercall
X1- X17 Bloque de parámetros de entrada

Si el bloque de parámetros de entrada es menor que 128 bytes, se omiten los bytes adicionales de los registros.

Salidas de Hypercall

Todas las hiperllamadas devuelven un valor de 64 bits denominado valor de resultado de hypercall. Tiene el formato siguiente:

Campo bits Comentario
Resultado 15-0 HV_STATUS código que indica éxito o error
Rsvd 31-16 Los autores de llamadas deben omitir el valor de estos bits
Representantes completados 43-32 Número de representantes completados correctamente
RsvdZ 63-40 Los autores de llamadas deben omitir el valor de estos bits

En el caso de las hiperllamadas de rep, el campo reps complete es el número total de reps completados y no en relación con el índice de inicio del rep. Por ejemplo, si el autor de la llamada especificó un índice de inicio de rep de 5 y un recuento de rep de 10, el campo reps complete indicaría 10 tras la finalización correcta.

El valor de resultado de hiperllamada se devuelve en los registros.

En x64, la asignación de registros depende de si el autor de la llamada se ejecuta en modo de 32 bits (x86) o de 64 bits (x64) (consulte más arriba). La asignación de registros para las salidas de hypercall es la siguiente:

x64 x86 Información proporcionada
RAX EDX:EAX Valor de resultado de Hypercall

En ARM64, la asignación de registros para las salidas de hypercall es la siguiente:

Register Información proporcionada
X0 Valor de resultado de Hypercall

Salida rápida de Hypercall de XMM (x86/x64)

De forma similar a la forma en que el hipervisor admite entradas de hiperllamada rápidas de XMM, se pueden compartir los mismos registros para devolver la salida. Esto solo se admite en plataformas x64.

La capacidad de devolver la salida a través de registros XMM se indica a través de la hoja de CPUID del hipervisor (0x40000003):

  • Bit 15: la compatibilidad con la devolución de la salida de hiperllamada a través de registros XMM está disponible.

Tenga en cuenta que hay una marca independiente para indicar la compatibilidad con la entrada rápida XMM. Cualquier intento de usar esta interfaz cuando el hipervisor no indica la disponibilidad producirá un error de #UD.

Asignación de registros (entrada y salida)

Los registros que no se usan para pasar parámetros de entrada se pueden usar para devolver la salida. En otras palabras, si el bloque de parámetros de entrada es menor que 112 bytes (redondeado hasta el fragmento alineado de 16 bytes más cercano), los registros restantes devolverán la salida de hypercall.

x64 Información proporcionada
RDX Bloque de entrada o salida
R8 Bloque de entrada o salida
XMM0 Bloque de entrada o salida
XMM1 Bloque de entrada o salida
XMM2 Bloque de entrada o salida
XMM3 Bloque de entrada o salida
XMM4 Bloque de entrada o salida
XMM5 Bloque de entrada o salida

Por ejemplo, si el bloque de parámetros de entrada tiene un tamaño de 20 bytes, el hipervisor omitiría los siguientes 12 bytes. Los 80 bytes restantes contendrán la salida de hiperllamada (si procede).

Registrar salida rápida de llamadas (ARM64 SMCCC)

En las plataformas ARM64, de forma similar a la forma en que el hipervisor admite el registro de entradas rápidas de hiperllamada, se pueden compartir los mismos registros para devolver la salida.

Asignación de registros (entrada y salida)

Los registros que no se usan para pasar parámetros de entrada se pueden usar para devolver la salida. En otras palabras, si el bloque de parámetros de entrada es menor que 128 bytes (redondeado hasta el fragmento alineado de 8 bytes más cercano), los registros restantes devolverán la salida de hypercall.

Register Información proporcionada
X2 - X17 Bloque de entrada o salida

Por ejemplo, si el bloque de parámetros de entrada tiene un tamaño de 20 bytes, el hipervisor omitiría los siguientes 4 bytes. Los 104 bytes restantes contendrán la salida de hiperllamada (si procede).

Registrar salida de llamada rápida (ARM64 HVC #1)

De forma similar a las versiones de SMCCC, la interfaz HVC #1 usa los mismos registros para devolver la salida.

Asignación de registros (entrada y salida)

Los registros que no se usan para pasar parámetros de entrada se pueden usar para devolver la salida. En otras palabras, si el bloque de parámetros de entrada es menor que 128 bytes (redondeado hasta el fragmento alineado de 8 bytes más cercano), los registros restantes devolverán la salida de hypercall.

Register Información proporcionada
X1- X17 Bloque de entrada o salida

Por ejemplo, si el bloque de parámetros de entrada tiene un tamaño de 20 bytes, el hipervisor omitiría los siguientes 4 bytes. Los 104 bytes restantes contendrán la salida de hiperllamada (si procede).

Registros volátiles (x86/x64)

Hypercalls solo modificará los valores de registro especificados en las condiciones siguientes:

  1. RAX (x64) y EDX:EAX (x86) siempre se sobrescriben con el valor de resultado de hiperllamada y los parámetros de salida, si los hay.
  2. Las hiperllamadas de rep modificarán RCX (x64) y EDX:EAX (x86) con el nuevo índice de inicio del rep.
  3. HvCallSetVpRegisters puede modificar los registros compatibles con esa hiperllamada.
  4. RDX, R8 y XMM0 a XMM5, cuando se usa para la entrada rápida de hiperllamada, permanecen sin modificar. Sin embargo, los registros usados para la salida rápida de hiperllamada se pueden modificar, incluidos RDX, R8 y XMM0 a XMM5. Hyper-V solo modificará estos registros para la salida rápida de hypercall, que se limita a x64.

Registros volátiles (ARM64 SMCCC)

Hypercalls solo modificará los valores de registro especificados en las condiciones siguientes:

  1. X0 siempre se sobrescribe con el valor de resultado de hypercall y los parámetros de salida, si los hay.
  2. Las hiperllamadas de rep modificarán X1 con el nuevo índice de inicio de la rep.
  3. HvCallSetVpRegisters puede modificar los registros compatibles con esa hiperllamada.
  4. X2: X17, cuando se usa para la entrada rápida de hiperllamada, permanece sin modificar. Sin embargo, los registros usados para la salida rápida de hiperllamada se pueden modificar, incluido X2 - X17. Hyper-V solo modificará estos registros para una salida rápida de hypercall.

Registros volátiles (ARM64 HVC #1)

Hypercalls solo modificará los valores de registro especificados en las condiciones siguientes:

  1. X0 siempre se sobrescribe con el valor de resultado de hypercall y los parámetros de salida, si los hay.
  2. Las hiperllamadas de rep modificarán X0 con el nuevo índice de inicio de la rep.
  3. HvCallSetVpRegisters puede modificar los registros compatibles con esa hiperllamada.
  4. X1: X17, cuando se usa para la entrada rápida de hiperllamada, permanece sin modificar. Sin embargo, se pueden modificar los registros usados para la salida rápida de hiperllamada, incluido X1 - X17. Hyper-V solo modificará estos registros para una salida rápida de hypercall.

Restricciones de hiperllamada

Las hiperllamadas pueden tener restricciones asociadas que deben cumplirse para que realicen su función prevista. Si no se cumplen todas las restricciones, la hiperllamada finalizará con un error adecuado. Se mostrarán las restricciones siguientes, si se aplica alguna:

  • La partición que llama debe poseer un privilegio determinado.
  • La partición en la que se actúa debe estar en un estado determinado (por ejemplo, "Activo")

Códigos de estado de Hypercall

Cada hiperllamada se documenta como devolver un valor de salida que contiene varios campos. Se usa un campo de valor de estado (de tipo HV_STATUS) para indicar si la llamada se realizó correctamente o no.

Validez del parámetro de salida en hiperllamadas con errores

A menos que se indique explícitamente lo contrario, cuando se produce un error en una hiperllamada (es decir, el campo de resultado del hiperllamada contiene un valor distinto HV_STATUS_SUCCESSde ), el contenido de todos los parámetros de salida es indeterminado y el autor de la llamada no debe examinarlo. Solo cuando la hiperllamada se realice correctamente, todos los parámetros de salida adecuados contendrán resultados válidos y esperados.

Ordenación de condiciones de error

El orden en el que el hipervisor detecta y notifica las condiciones de error no está definido. En otras palabras, si existen varios errores, el hipervisor debe elegir qué condición de error se va a notificar. Se debe dar prioridad a esos códigos de error que ofrecen mayor seguridad, la intención de evitar que el hipervisor revele información a los autores de llamadas que carecen de privilegios suficientes. Por ejemplo, el código HV_STATUS_ACCESS_DENIED de estado es el código de estado preferido sobre uno que revelaría información de contexto o estado basada exclusivamente en privilegios.

Códigos comunes de estado de Hypercall

Varios códigos de resultado son comunes a todas las hiperllamadas y, por tanto, no se documentan para cada hiperllamada individualmente. Estos incluyen lo siguiente:

Código de estado Condición de error
HV_STATUS_SUCCESS La llamada se realizó correctamente.
HV_STATUS_INVALID_HYPERCALL_CODE No se reconoce el código de hiperllamada.
HV_STATUS_INVALID_HYPERCALL_INPUT El recuento de representantes es incorrecto (por ejemplo, se pasa un recuento de rep distinto de cero a una llamada que no es de rep o se pasa un recuento de representantes cero a una llamada de rep).
El índice de inicio de la rep no es menor que el recuento de rep.
Un bit reservado en el valor de entrada de hiperllamada especificado es distinto de cero.
HV_STATUS_INVALID_ALIGNMENT El puntero GPA de entrada o salida especificado no está alineado a 8 bytes.
Las listas de parámetros de entrada o salida especificadas abarcan páginas.
El puntero GPA de entrada o salida no está dentro de los límites del espacio GPA.

El código HV_STATUS_SUCCESS de retorno indica que no se detectó ninguna condición de error.

Notificación de la identidad del sistema operativo invitado

El sistema operativo invitado que se ejecuta dentro de la partición debe identificarse en el hipervisor escribiendo su firma y versión en un MSR (HV_X64_MSR_GUEST_OS_ID/HvRegisterGuestOsId) para poder invocar hiperllamadas. Este MSR es de toda la partición y se comparte entre todos los procesadores virtuales.

El valor de este registro es inicialmente cero.

En x86/x64, se debe escribir un valor distinto de cero en el MSR de id. de so invitado antes de que se pueda habilitar la página de códigos de hiperllamada (consulte Establecimiento de la interfaz de Hypercall (x86/x64)). Si este registro se aplica posteriormente a ceros, se deshabilitará la página de códigos de hypercall.

En ARM64, se debe escribir un valor distinto de cero en el MSR id. del sistema operativo invitado antes de que se puedan invocar códigos de hiperllamada. La excepción es el hiperllamada HvCallSetVpRegisters/HvCallGetVpRegisters . Consulte su documentación correspondiente para obtener más información.

#define HV_X64_MSR_GUEST_OS_ID 0x40000000
#define HvRegisterGuestOsId 0x00090002

En ARM64, solo se admite HvRegisterGuestOsId, que se debe escribir mediante la hiperllamada HvCallSetVpRegisters en el procesador de arranque.

Identidad del sistema operativo invitado para sistemas operativos propietarios

A continuación se muestra la codificación recomendada para este MSR. Es posible que algunos campos no se apliquen a algunos sistemas operativos invitados.

bits Campo Description
15:0 Número de compilación Indica el número de compilación del sistema operativo.
23:16 Versión del servicio Indica la versión del servicio (por ejemplo, el número "Service Pack")
31:24 Versión Menor Indica la versión secundaria del sistema operativo.
39:32 Versión Principal Indica la versión principal del sistema operativo.
47:40 Identificador del sistema operativo Indica la variante del sistema operativo. La codificación es única para el proveedor. Los sistemas operativos Microsoft se codifican de la siguiente manera: 0=Undefined, 1=MS-DOS®, 2=Windows® 3.x, 3=Windows® 9x, 4=Windows® NT (y derivados), 5=Windows® CE
62:48 Id. de proveedor Indica el proveedor del sistema operativo invitado. Se reserva un valor de 0. Consulte la lista de proveedores a continuación.
63 Tipo de SO Indica el tipo de sistema operativo. Un valor de 0 representa un sistema operativo propietario (de origen cerrado). Un valor de 1 representa un sistema operativo de código abierto.

Microsoft asigna valores de proveedor. Para solicitar un nuevo proveedor, envíe un problema en el repositorio de documentación de virtualización de GitHub (https://aka.ms/VirtualizationDocumentationIssuesTLFS).

Proveedor Importancia
Microsoft 0x0001
HPE 0x0002
BlackBerry 0x0003
LANCOM 0x0200

MSR de identidad del sistema operativo invitado para sistemas operativos de código abierto

La siguiente codificación se ofrece como guía para los proveedores de sistemas operativos de código abierto que pretenden cumplir esta especificación. Se recomienda que los sistemas operativos de código abierto adopten la convención siguiente.

bits Campo Description
15:0 Número de compilación Información específica de la distribución (por ejemplo, número de compilación).
47:16 Versión Información de la versión del kernel ascendente.
55:48 Identificador del sistema operativo Información adicional del proveedor
62:56 Tipo de SO Tipo de sistema operativo (por ejemplo, Linux, FreeBSD, etc.). Consulte la lista de tipos de sistema operativo conocidos a continuación.
63 Código abierto Un valor de 1 indica un sistema operativo de código abierto.

Microsoft asigna los valores de tipo de sistema operativo. Para solicitar un nuevo tipo de sistema operativo, envíe un problema en el repositorio de documentación de virtualización de GitHub (https://aka.ms/VirtualizationDocumentationIssuesTLFS).

Tipo de SO Importancia
Linux 0x1
FreeBSD 0x2
Xen 0x3
Illumos 0x4

Establecimiento de la interfaz hypercall (x86/x64)

En x86/x64, las llamadas a Hypercall se invocan mediante un código de operación especial. Dado que este código de operación difiere entre las implementaciones de virtualización, es necesario que el hipervisor abstraa esta diferencia. Esto se hace a través de una página de hiperllamada especial. El hipervisor proporciona esta página y aparece dentro del espacio GPA del invitado. El invitado es necesario para especificar la ubicación de la página mediante la programación de MSR de Hypercall invitado.

#define HV_X64_MSR_HYPERCALL 0x40000001
bits Description Attributes
63:12 GPFN de Hypercall: indica el número de página física invitada de la página de hiperllamada. Lectura/escritura
11:2 RsvdP. Los bits deben omitirse en lecturas y conservarse en escrituras. Reservado
1 Cerrado con llave. Indica si MSR es inmutable. Si se establece, este MSR se bloquea, lo que impide la reubicación de la página de hiperllamada. Una vez establecido, solo un restablecimiento del sistema puede borrar el bit. Lectura/escritura
0 Habilitar la página de hypercall Lectura/escritura

La página de hiperllamada se puede colocar en cualquier lugar dentro del espacio de GPA del invitado, pero debe estar alineada con páginas. Si el invitado intenta mover la página de hiperllamada más allá de los límites del espacio de GPA, se producirá un error de #GP cuando se escriba msr.

Este MSR es un MSR de toda la partición. En otras palabras, todos los procesadores virtuales comparten la partición. Si un procesador virtual escribe correctamente en MSR, otro procesador virtual leerá el mismo valor.

Antes de habilitar la página de hiperllamada, el sistema operativo invitado debe notificar su identidad escribiendo su firma de versión en un MSR independiente (HV_X64_MSR_GUEST_OS_ID). Si no se ha especificado ninguna identidad del sistema operativo invitado, se producirá un error en los intentos de habilitar la hiperllamada. El bit de habilitación permanecerá cero incluso al escribir uno en él. Además, si la identidad del sistema operativo invitado se borra a cero después de habilitar la página de hiperllamada, se deshabilitará.

La página de hiperllamada aparece como una "superposición" al espacio de GPA; es decir, cubre lo que se asigne al intervalo de GPA. El invitado puede leer y ejecutable su contenido. Los intentos de escritura en la página de hiperllamada producirán una excepción de protección (#GP). Una vez habilitada la página de hiperllamada, invocar una hiperllamada simplemente implica una llamada al inicio de la página.

A continuación se muestra una lista detallada de los pasos necesarios para establecer la página de hiperllamada:

  1. El invitado lee la hoja 1 de CPUID y determina si un hipervisor está presente comprobando el bit 31 del registro ECX.
  2. El invitado lee el 0x40000000 hoja CPUID para determinar la hoja de CPUID máxima del hipervisor (que se devuelve en el registro EAX) y la hoja de CPUID 0x40000001 para determinar la firma de interfaz (que se devuelve en el registro EAX). Comprueba que el valor máximo de hoja es al menos 0x40000005 y que la firma de interfaz es igual a "Hv#1". Esta firma implica que HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL y HV_X64_MSR_VP_INDEX se implementan.
  3. El invitado escribe su identidad del sistema operativo en MSR HV_X64_MSR_GUEST_OS_ID si ese registro es cero.
  4. El invitado lee msr de llamada de Hypercall (HV_X64_MSR_HYPERCALL).
  5. El invitado comprueba el bit Habilitar la página de Hypercall. Si se establece, la interfaz ya está activa y se deben omitir los pasos 6 y 7.
  6. El invitado encuentra una página dentro de su espacio de GPA, preferiblemente una que no está ocupada por RAM, MMIO, etc. Si la página está ocupada, el invitado debe evitar el uso de la página subyacente para otros fines.
  7. El invitado escribe un nuevo valor en el MSR de Hypercall (HV_X64_MSR_HYPERCALL) que incluye el GPA del paso 6 y establece el bit Habilitar la página de Hypercall para habilitar la interfaz.
  8. El invitado crea una asignación de VA ejecutable a la GPA de la página hypercall.
  9. El invitado consulta el 0x40000003 hoja CPUID para determinar qué instalaciones de hipervisor están disponibles para él. Una vez establecida la interfaz, el invitado puede iniciar una hiperllamada. Para ello, rellena los registros según el protocolo hypercall y emite una LLAMADA al principio de la página de hypercall. El invitado debe suponer que la página de hypercall realiza el equivalente de un retorno cercano (0xC3) para volver al autor de la llamada. Por lo tanto, la hiperllamada debe invocarse con una pila válida.

Establecimiento de la interfaz hypercall (ARM64)

Dado que ARM64 admite de forma nativa la instrucción HVC, el hipervisor no necesita configuración adicional para habilitar las hiperllamadas.

Interfaz de Hypercall extendida

Las hiperllamadas con códigos de llamada anteriores 0x8000 se conocen como hiperllamadas extendidas. Las hiperllamadas extendidas usan la misma convención de llamada que las hiperllamadas normales y aparecen idénticas desde la perspectiva de una máquina virtual invitada. Las hipercalles extendidas se controlan internamente de forma diferente dentro del hipervisor de Hyper-V.

Las funcionalidades extendidas de hypercall se pueden consultar con HvExtCallQueryCapabilities.