Game / editor prototyping with Godot
1# QEMURunner.gd
2# Alpine Linux ARM64 QEMU Runner as a Node
3# Add to your scene and call run_vm()
4
5extends Node
6
7# Configuration
8const ALP_ARCH = "aarch64"
9const PORT = "2221"
10var DISK_NAME = "alpine.%s.qcow2" % ALP_ARCH
11
12# Working directory - qemu/ folder in project root
13var working_dir: String = ""
14
15@export var run_button: Button
16@export var code_edit: CodeEdit
17
18signal vm_started()
19signal vm_stopped(exit_code: int)
20signal vm_error(error_message: String)
21
22var qemu_pid: int = -1
23
24func handle_run():
25 print(code_edit.text)
26
27func _ready():
28 # Set working directory to PROJECT_ROOT/qemu/
29 working_dir = ProjectSettings.globalize_path("res://qemu/")
30 print("QEMU working directory: %s" % working_dir)
31
32 # Create qemu directory if it doesn't exist
33 if not DirAccess.dir_exists_absolute(working_dir):
34 DirAccess.make_dir_recursive_absolute(working_dir)
35 print("Created qemu directory: %s" % working_dir)
36
37 run_vm()
38
39 run_button.button_up.connect(handle_run)
40
41func _exit_tree() -> void:
42 stop_vm()
43
44func check_file_exists(filename: String) -> bool:
45 """Check if required file exists in working directory"""
46 var full_path = working_dir.path_join(filename)
47 print(full_path)
48 if not FileAccess.file_exists(full_path):
49 print("Error: %s not found!" % full_path)
50 return false
51 return true
52
53func run_vm():
54 print("Alpine Linux ARM64 QEMU Runner")
55 print()
56
57 # Check required files
58 var required_files = [
59 "AAVMF_CODE.fd",
60 DISK_NAME
61 ]
62
63 var missing_files = []
64 for file in required_files:
65 if not check_file_exists(file):
66 missing_files.append(file)
67
68 if not missing_files.is_empty():
69 print("\nMissing required files:")
70 for file in missing_files:
71 print(" - %s" % file)
72 print("\nPlease run the setup script first to create these files.")
73 vm_error.emit("Missing required files: " + str(missing_files))
74 return
75
76 # Display connection info
77 print("Starting Alpine Linux VM...")
78 print("SSH Port: %s" % PORT)
79 print("Connect with: ssh -p %s <username>@localhost" % PORT)
80 print()
81 print("To exit QEMU: Press Ctrl+A then X")
82 print()
83
84 # Build QEMU command
85 var qemu_cmd = PackedStringArray([
86 "qemu-system-aarch64",
87 "-accel", "tcg,thread=multi",
88 "-machine", "virt",
89 "-cpu", "cortex-a53",
90 "-smp", "cores=1",
91 "-m", "512",
92 "-bios", working_dir.path_join("AAVMF_CODE.fd"),
93 "-nographic",
94 "-drive", "if=virtio,id=hd,format=qcow2,file=%s" % working_dir.path_join(DISK_NAME),
95 "-nic", "user,hostfwd=tcp::%s-:22,model=virtio" % PORT,
96 "-device", "virtio-rng-pci",
97 "-rtc", "base=utc,clock=host",
98 "-s"
99 ])
100
101 # Run QEMU
102 vm_started.emit()
103
104 qemu_pid = OS.create_process(qemu_cmd[0], qemu_cmd.slice(1))
105
106 if qemu_pid == -1:
107 print("\nError: qemu-system-aarch64 not found in PATH")
108 print("Please install QEMU for Windows: https://qemu.weilnetz.de/w64/")
109 print("Make sure qemu-system-aarch64 is in your system PATH")
110 vm_error.emit("Failed to start QEMU process")
111 return
112
113 print("QEMU started with PID: %d" % qemu_pid)
114
115func stop_vm():
116 print("Attempting to kill QEMU")
117 if qemu_pid != -1:
118 OS.kill(qemu_pid)
119 print("QEMU stopped")
120 vm_stopped.emit(0)
121 qemu_pid = -1