├── .gitignore ├── subprojects ├── phosphor-logging.wrap ├── packagefiles │ └── boost │ │ └── meson.build └── boost.wrap ├── obmc-ikvm.cpp ├── obmc-ikvm.service ├── meson.build ├── OWNERS ├── ikvm_manager.cpp ├── ikvm_manager.hpp ├── README.md ├── ikvm_args.cpp ├── scancodes.hpp ├── .clang-format ├── ikvm_args.hpp ├── ikvm_input.hpp ├── ikvm_video.hpp ├── ikvm_server.hpp ├── create_usbhid.sh ├── ikvm_server.cpp ├── ikvm_video.cpp ├── ikvm_input.cpp └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | subprojects/* 3 | !subprojects/phosphor-logging.wrap 4 | !subprojects/boost.wrap 5 | !subprojects/packagefiles 6 | -------------------------------------------------------------------------------- /subprojects/phosphor-logging.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | url = https://github.com/openbmc/phosphor-logging.git 3 | revision = HEAD 4 | 5 | [provide] 6 | phosphor-logging = phosphor_logging_dep 7 | -------------------------------------------------------------------------------- /subprojects/packagefiles/boost/meson.build: -------------------------------------------------------------------------------- 1 | project('boost', 'cpp', version: '1.84.0', license: 'Boost') 2 | 3 | boost_dep = declare_dependency(include_directories: include_directories('.')) 4 | 5 | meson.override_dependency('boost', boost_dep) 6 | -------------------------------------------------------------------------------- /obmc-ikvm.cpp: -------------------------------------------------------------------------------- 1 | #include "ikvm_args.hpp" 2 | #include "ikvm_manager.hpp" 3 | 4 | int main(int argc, char* argv[]) 5 | { 6 | ikvm::Args args(argc, argv); 7 | ikvm::Manager manager(args); 8 | 9 | manager.run(); 10 | 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /obmc-ikvm.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=OpenBMC ipKVM daemon 3 | ConditionPathIsMountPoint=/sys/kernel/config 4 | 5 | [Service] 6 | Restart=always 7 | ExecStartPre=/usr/bin/create_usbhid.sh disconnect 8 | ExecStart=/usr/bin/obmc-ikvm -v /dev/video0 -k /dev/hidg0 -p /dev/hidg1 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /subprojects/boost.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = boost_1_84_0 3 | 4 | source_url = https://boostorg.jfrog.io/artifactory/main/release/1.84.0/source/boost_1_84_0.tar.bz2 5 | source_hash = cc4b893acf645c9d4b698e9a0f08ca8846aa5d6c68275c14c3e7949c24109454 6 | source_filename = 1_84_0.tar.bz2 7 | 8 | patch_directory = boost 9 | 10 | [provide] 11 | boost = boost_dep 12 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'obmc-ikvm', 3 | 'cpp', 4 | version: '1.0', 5 | license: 'GPLv2', 6 | default_options: ['cpp_std=c++23', 'werror=true'], 7 | meson_version: '>=1.1.1', 8 | ) 9 | 10 | install_data( 11 | 'create_usbhid.sh', 12 | install_mode: 'rwxr-xr-x', 13 | install_dir: get_option('bindir'), 14 | ) 15 | 16 | executable( 17 | 'obmc-ikvm', 18 | [ 19 | 'ikvm_args.cpp', 20 | 'ikvm_input.cpp', 21 | 'ikvm_manager.cpp', 22 | 'ikvm_server.cpp', 23 | 'ikvm_video.cpp', 24 | 'obmc-ikvm.cpp', 25 | ], 26 | dependencies: [ 27 | dependency('libvncserver'), 28 | dependency('phosphor-logging'), 29 | dependency('phosphor-dbus-interfaces'), 30 | dependency('sdbusplus'), 31 | dependency('threads'), 32 | dependency('boost'), 33 | ], 34 | install: true, 35 | ) 36 | 37 | fs = import('fs') 38 | fs.copyfile( 39 | 'obmc-ikvm.service', 40 | install: true, 41 | install_dir: dependency('systemd').get_variable('systemd_system_unit_dir'), 42 | ) 43 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | # OWNERS 2 | # ------ 3 | # 4 | # The OWNERS file maintains the list of individuals responsible for various 5 | # parts of this repository, including code review and approval. We use the 6 | # Gerrit 'owners' plugin, which consumes this file, along with some extra 7 | # keywords for our own purposes and tooling. 8 | # 9 | # For details on the configuration used by 'owners' see: 10 | # https://gerrit.googlesource.com/plugins/owners/+/refs/heads/master/owners/src/main/resources/Documentation/config.md 11 | # 12 | # An OWNERS file must be in the root of a repository but may also be present 13 | # in any subdirectory. The contents of the subdirectory OWNERS file are 14 | # combined with parent directories unless 'inherit: false' is set. 15 | # 16 | # The owners file is YAML and has [up to] 4 top-level keywords. 17 | # * owners: A list of individuals who have approval authority on the 18 | # repository. 19 | # 20 | # * reviewers: A list of individuals who have requested review notification 21 | # on the repository. 22 | # 23 | # * matchers: A list of specific file/path matchers for granular 'owners' and 24 | # 'reviewers'. See 'owners' plugin documentation. 25 | # 26 | # * openbmc: A list of openbmc-specific meta-data about owners and reviewers. 27 | # - name: preferred name of the individual. 28 | # - email: preferred email address of the individual. 29 | # - discord: Discord nickname of the individual. 30 | # 31 | # It is expected that these 4 sections will be listed in the order above and 32 | # data within them will be kept sorted. 33 | 34 | owners: 35 | - eajames@linux.ibm.com 36 | - jae.hyun.yoo@linux.intel.com 37 | 38 | reviewers: 39 | 40 | matchers: 41 | 42 | openbmc: 43 | - name: Eddie James 44 | email: eajames@linux.ibm.com 45 | discord: eajames 46 | - name: Jae Hyun Yoo 47 | email: jae.hyun.yoo@linux.intel.com 48 | discord: jae 49 | -------------------------------------------------------------------------------- /ikvm_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "ikvm_manager.hpp" 2 | 3 | #include 4 | 5 | namespace ikvm 6 | { 7 | Manager::Manager(const Args& args) : 8 | continueExecuting(true), serverDone(false), videoDone(true), 9 | input(args.getKeyboardPath(), args.getPointerPath(), args.getUdcName()), 10 | video(args.getVideoPath(), input, args.getFrameRate(), 11 | args.getSubsampling()), 12 | server(args, input, video) 13 | {} 14 | 15 | void Manager::run() 16 | { 17 | std::thread run(serverThread, this); 18 | 19 | while (continueExecuting) 20 | { 21 | if (server.wantsFrame()) 22 | { 23 | video.start(); 24 | video.getFrame(); 25 | server.sendFrame(); 26 | } 27 | else 28 | { 29 | video.stop(); 30 | } 31 | 32 | if (video.needsResize()) 33 | { 34 | waitServer(); 35 | videoDone = false; 36 | video.resize(); 37 | server.resize(); 38 | setVideoDone(); 39 | } 40 | else 41 | { 42 | setVideoDone(); 43 | waitServer(); 44 | } 45 | } 46 | 47 | run.join(); 48 | } 49 | 50 | void Manager::serverThread(Manager* manager) 51 | { 52 | while (manager->continueExecuting) 53 | { 54 | manager->server.run(); 55 | manager->setServerDone(); 56 | manager->waitVideo(); 57 | } 58 | } 59 | 60 | void Manager::setServerDone() 61 | { 62 | std::unique_lock ulock(lock); 63 | 64 | serverDone = true; 65 | sync.notify_all(); 66 | } 67 | 68 | void Manager::setVideoDone() 69 | { 70 | std::unique_lock ulock(lock); 71 | 72 | videoDone = true; 73 | sync.notify_all(); 74 | } 75 | 76 | void Manager::waitServer() 77 | { 78 | std::unique_lock ulock(lock); 79 | 80 | while (!serverDone) 81 | { 82 | sync.wait(ulock); 83 | } 84 | 85 | serverDone = false; 86 | } 87 | 88 | void Manager::waitVideo() 89 | { 90 | std::unique_lock ulock(lock); 91 | 92 | while (!videoDone) 93 | { 94 | sync.wait(ulock); 95 | } 96 | 97 | // don't reset videoDone 98 | } 99 | 100 | } // namespace ikvm 101 | -------------------------------------------------------------------------------- /ikvm_manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ikvm_args.hpp" 4 | #include "ikvm_input.hpp" 5 | #include "ikvm_server.hpp" 6 | #include "ikvm_video.hpp" 7 | 8 | #include 9 | #include 10 | 11 | namespace ikvm 12 | { 13 | /* 14 | * @class Manager 15 | * @brief Manages the VNC server by executing threaded loops of RFB operations 16 | * and video device operations. 17 | */ 18 | class Manager 19 | { 20 | public: 21 | /* 22 | * @brief Constructs the Manager object 23 | * 24 | * @param[in] args - Reference to Args object 25 | */ 26 | explicit Manager(const Args& args); 27 | ~Manager() = default; 28 | Manager(const Manager&) = default; 29 | Manager& operator=(const Manager&) = default; 30 | Manager(Manager&&) = default; 31 | Manager& operator=(Manager&&) = default; 32 | 33 | /* @brief Begins operation of the VNC server */ 34 | void run(); 35 | 36 | private: 37 | /* 38 | * @brief Thread function to loop the RFB update operations 39 | * 40 | * @param[in] manager - Pointer to the Manager object 41 | */ 42 | static void serverThread(Manager* manager); 43 | 44 | /* @brief Notifies thread waiters that RFB operations are complete */ 45 | void setServerDone(); 46 | /* @brief Notifies thread waiters that video operations are complete */ 47 | void setVideoDone(); 48 | /* @brief Blocks until RFB operations complete */ 49 | void waitServer(); 50 | /* @brief Blocks until video operations are complete */ 51 | void waitVideo(); 52 | 53 | /* 54 | * @brief Boolean to indicate whether the application should continue 55 | * running 56 | */ 57 | bool continueExecuting; 58 | /* @brief Boolean to indicate that RFB operations are complete */ 59 | bool serverDone; 60 | /* @brief Boolean to indicate that video operations are complete */ 61 | bool videoDone; 62 | /* @brief Input object */ 63 | Input input; 64 | /* @brief Video object */ 65 | Video video; 66 | /* @brief RFB server object */ 67 | Server server; 68 | /* @brief Condition variable to enable waiting for thread completion */ 69 | std::condition_variable sync; 70 | /* @brief Mutex for waiting on condition variable safely */ 71 | std::mutex lock; 72 | }; 73 | 74 | } // namespace ikvm 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenBMC IpKVM Server 2 | 3 | The obmc-ikvm application is a VNC server that provides access to the host 4 | graphics output. The application interfaces with the video device on the BMC 5 | that captures the host graphics, and then serves that video data on the RFB 6 | (remote framebuffer, also known as VNC) protocol. The application also 7 | interfaces with the BMC USB gadget device to pass HID events from the BMC to the 8 | host, allowing the user to interact with the host system. 9 | 10 | ## Usage 11 | 12 | Once the host is running and an appropriate HID gadget device is instantiated on 13 | the BMC, the application can be started with the following command: 14 | `obmc-ikvm -v