| /* |
| * Copyright 2018 The Hafnium Authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include "hf/spci.h" |
| #include "hf/types.h" |
| |
| enum hf_vcpu_run_code { |
| /** |
| * The vCPU has been preempted but still has work to do. If the |
| * scheduling quantum has not expired, the scheduler MUST call |
| * `hf_vcpu_run` on the vCPU to allow it to continue. |
| */ |
| HF_VCPU_RUN_PREEMPTED = 0, |
| |
| /** |
| * The vCPU has voluntarily yielded the CPU. The scheduler SHOULD take a |
| * scheduling decision to give cycles to those that need them but MUST |
| * call `hf_vcpu_run` on the vCPU at a later point. |
| */ |
| HF_VCPU_RUN_YIELD = 1, |
| |
| /** |
| * The vCPU is blocked waiting for an interrupt. The scheduler MUST take |
| * it off the run queue and not call `hf_vcpu_run` on the vCPU until it |
| * has injected an interrupt, received `HF_VCPU_RUN_WAKE_UP` for it |
| * from another vCPU or the timeout provided in |
| * `hf_vcpu_run_return.sleep` is not `HF_SLEEP_INDEFINITE` and the |
| * specified duration has expired. |
| */ |
| HF_VCPU_RUN_WAIT_FOR_INTERRUPT = 2, |
| |
| /** |
| * The vCPU is blocked waiting for a message. The scheduler MUST take it |
| * off the run queue and not call `hf_vcpu_run` on the vCPU until it has |
| * injected an interrupt, sent it a message, or received |
| * `HF_VCPU_RUN_WAKE_UP` for it from another vCPU, or the timeout |
| * provided in `hf_vcpu_run_return.sleep` is not `HF_SLEEP_INDEFINITE` |
| * and the specified duration has expired. |
| */ |
| HF_VCPU_RUN_WAIT_FOR_MESSAGE = 3, |
| |
| /** |
| * Hafnium would like `hf_vcpu_run` to be called on another vCPU, |
| * specified by `hf_vcpu_run_return.wake_up`. The scheduler MUST either |
| * wake the vCPU in question up if it is blocked, or preempt and re-run |
| * it if it is already running somewhere. This gives Hafnium a chance to |
| * update any CPU state which might have changed. |
| */ |
| HF_VCPU_RUN_WAKE_UP = 4, |
| |
| /** |
| * A message has been sent by the vCPU. The scheduler MUST run a vCPU |
| * from the recipient VM and priority SHOULD be given to those vCPUs |
| * that are waiting for a message. |
| */ |
| HF_VCPU_RUN_MESSAGE = 5, |
| |
| /** |
| * The vCPU has made the mailbox writable and there are pending waiters. |
| * The scheduler MUST call hf_mailbox_waiter_get() repeatedly and notify |
| * all waiters by injecting an HF_MAILBOX_WRITABLE_INTID interrupt. |
| */ |
| HF_VCPU_RUN_NOTIFY_WAITERS = 6, |
| |
| /** |
| * The vCPU has aborted triggering the whole VM to abort. The scheduler |
| * MUST treat this as `HF_VCPU_RUN_WAIT_FOR_INTERRUPT` for this vCPU and |
| * `HF_VCPU_RUN_WAKE_UP` for all the other vCPUs of the VM. |
| */ |
| HF_VCPU_RUN_ABORTED = 7, |
| }; |
| |
| struct hf_vcpu_run_return { |
| enum hf_vcpu_run_code code; |
| union { |
| struct { |
| spci_vm_id_t vm_id; |
| spci_vcpu_index_t vcpu; |
| } wake_up; |
| struct { |
| spci_vm_id_t vm_id; |
| uint32_t size; |
| } message; |
| struct { |
| uint64_t ns; |
| } sleep; |
| }; |
| }; |
| |
| enum hf_share { |
| /** |
| * Relinquish ownership and access to the memory and pass them to the |
| * recipient. |
| */ |
| HF_MEMORY_GIVE, |
| |
| /** |
| * Retain ownership of the memory but relinquish access to the |
| * recipient. |
| */ |
| HF_MEMORY_LEND, |
| |
| /** |
| * Retain ownership and access but additionally allow access to the |
| * recipient. |
| */ |
| HF_MEMORY_SHARE, |
| }; |
| |
| /** |
| * Encode an hf_vcpu_run_return struct in the 64-bit packing ABI. |
| */ |
| static inline uint64_t hf_vcpu_run_return_encode(struct hf_vcpu_run_return res) |
| { |
| uint64_t ret = res.code & 0xff; |
| |
| switch (res.code) { |
| case HF_VCPU_RUN_WAKE_UP: |
| ret |= (uint64_t)res.wake_up.vm_id << 32; |
| ret |= (uint64_t)res.wake_up.vcpu << 16; |
| break; |
| case HF_VCPU_RUN_MESSAGE: |
| ret |= (uint64_t)res.message.size << 32; |
| ret |= res.message.vm_id << 8; |
| break; |
| case HF_VCPU_RUN_WAIT_FOR_INTERRUPT: |
| case HF_VCPU_RUN_WAIT_FOR_MESSAGE: |
| ret |= res.sleep.ns << 8; |
| break; |
| default: |
| break; |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Decode an hf_vcpu_run_return struct from the 64-bit packing ABI. |
| */ |
| static inline struct hf_vcpu_run_return hf_vcpu_run_return_decode(uint64_t res) |
| { |
| struct hf_vcpu_run_return ret = { |
| .code = (enum hf_vcpu_run_code)(res & 0xff), |
| }; |
| |
| /* Some codes include more data. */ |
| switch (ret.code) { |
| case HF_VCPU_RUN_WAKE_UP: |
| ret.wake_up.vm_id = res >> 32; |
| ret.wake_up.vcpu = (res >> 16) & 0xffff; |
| break; |
| case HF_VCPU_RUN_MESSAGE: |
| ret.message.size = res >> 32; |
| ret.message.vm_id = (res >> 8) & 0xffff; |
| break; |
| case HF_VCPU_RUN_WAIT_FOR_INTERRUPT: |
| case HF_VCPU_RUN_WAIT_FOR_MESSAGE: |
| ret.sleep.ns = res >> 8; |
| break; |
| default: |
| break; |
| } |
| |
| return ret; |
| } |