1#!/usr/bin/python 2# 3# Copyright (C) 2020 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import json 19import os 20import sys 21 22# If this doesn't help use PYTHONPATH=${PYTHONPATH}:<berberis>/android_api 23sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), '..', '..', 'android_api')) 24 25import api_analysis 26 27 28def _merge_syscalls(all_syscalls, custom_syscalls): 29 arch_names = {'arm', 'arm64', 'x86', 'x86_64', 'riscv64'} 30 31 for name, custom_syscall in custom_syscalls.items(): 32 # Top-level fields apply to all architecures 33 common_update = {k: v for k, v in custom_syscall.items() if k not in arch_names} 34 35 for arch in arch_names: 36 arch_update = custom_syscall.get(arch, {}) 37 38 # Syscall for arch does not exist. 39 # Create it only if update for arch is not empty. 40 # Do not create syscall for arch from common update only! 41 if not arch_update: 42 if name not in all_syscalls or arch not in all_syscalls[name]: 43 continue 44 45 arch_syscall = all_syscalls.setdefault(name, {}).setdefault(arch, {}) 46 arch_syscall.update(common_update) 47 arch_syscall.update(arch_update) 48 49 50def _get_syscall_params(arch_syscall, arch_api): 51 if 'params' in arch_syscall: 52 return arch_syscall['params'] 53 54 if 'entry' not in arch_syscall: 55 return None 56 57 entry_symbol_name = '__do_' + arch_syscall['entry'] 58 if entry_symbol_name not in arch_api['symbols']: 59 return None 60 entry_symbol = arch_api['symbols'][entry_symbol_name] 61 62 entry_type = arch_api['types'][entry_symbol['type']] 63 assert entry_type['kind'] == 'function' 64 return entry_type['params'] 65 66 67def _is_syscall_api_compatible(src_syscall, src_api): 68 for param_type_name in _get_syscall_params(src_syscall, src_api): 69 assert param_type_name in src_api['types'] 70 if not src_api['types'][param_type_name]['is_compatible']: 71 return False 72 73 return True 74 75 76def _is_64bit_arch(arch): 77 return arch == 'arm64' or arch == 'x86_64' or arch == 'riscv64' 78 79def _match_syscall_args(src_arch, dst_arch, src_api, params): 80 if _is_64bit_arch(src_arch) and _is_64bit_arch(dst_arch): 81 args = ['arg_%d' % (i + 1) for i in range(len(params))] 82 return args, args 83 84 assert src_arch == 'arm' and dst_arch == 'x86' 85 86 src_args = [] 87 dst_args = [] 88 89 for param_type_name in params: 90 param_type = src_api['types'][param_type_name] 91 param_size = int(param_type['size']) 92 93 i = len(src_args) 94 if param_size <= 32: 95 src_args.append('arg_%d' % (i + 1)) 96 dst_args.append('arg_%d' % (i + 1)) 97 else: 98 assert param_size == 64 99 if i % 2 != 0: 100 src_args.append('arg_%d' % (i + 1)) 101 i += 1 102 src_args.append('arg_%d' % (i + 1)) 103 src_args.append('arg_%d' % (i + 2)) 104 dst_args.append('arg_%d' % (i + 1)) 105 dst_args.append('arg_%d' % (i + 2)) 106 107 return src_args, dst_args 108 109 110def _print_syscalls_translation(src_arch, dst_arch, all_syscalls, guest_api): 111 print("""\ 112// This file automatically generated by gen_kernel_syscalls_translation.py 113// DO NOT EDIT! 114 115long RunGuestSyscallImpl(long guest_nr, 116 long arg_1, 117 long arg_2, 118 long arg_3, 119 long arg_4, 120 long arg_5, 121 long arg_6) { 122 switch (guest_nr) {""") 123 124 # Sort syscalls by name 125 for name, syscall in sorted(all_syscalls.items()): 126 # Filter syscalls by src_arch 127 if src_arch not in syscall: 128 continue 129 src_syscall = syscall[src_arch] 130 131 print(' case %s: // %s' % (src_syscall['id'], name)) 132 133 params = _get_syscall_params(src_syscall, guest_api) 134 if params is None: 135 print(' // missing prototype') 136 print(' TRACE("unsupported syscall %s");' % (name)) 137 print(' errno = ENOSYS;') 138 print(' return -1;') 139 continue 140 141 # Guest args might differ from host args. 142 # For example, arm aligns register pairs for 64-bit args and x86 doesn't. 143 # For arm to x86, matching should remove padding - skip certain args. 144 # For x86 to arm, matching should insert padding - insert zero args. 145 # Host system call obiously receives _host_ args. 146 # For RunGuestSyscall_* we need a convention. Assume we compile same guest for multiple hosts, 147 # then guest args are stable while host args might vary. For better source compatibility, let 148 # RunGuestSyscall_* always receive _guest_ args. 149 src_args, dst_args = _match_syscall_args(src_arch, dst_arch, guest_api, params) 150 151 # Translate to syscall with the same name. 152 # This is a simplification, but it works for architectures of the same bitness. 153 if dst_arch not in syscall: 154 print(' // missing on %s' % (dst_arch)) 155 print(' return RunGuestSyscall_%s(' % (name) + ', '.join(src_args) + ');') 156 continue 157 158 dst_syscall = syscall[dst_arch] 159 160 if 'custom_reason' in src_syscall: 161 print(' // %s' % (src_syscall['custom_reason'])) 162 print(' return RunGuestSyscall_%s(' % (name) + ', '.join(src_args) + ');') 163 continue 164 165 if 'custom_reason' in dst_syscall: 166 print(' // %s' % (dst_syscall['custom_reason'])) 167 print(' return RunGuestSyscall_%s(' % (name) + ', '.join(src_args) + ');') 168 continue 169 170 # If no custom reason given, syscall must have ids and entries. 171 assert 'id' in src_syscall 172 assert 'id' in dst_syscall 173 assert 'entry' in src_syscall 174 assert 'entry' in dst_syscall 175 176 if not _is_syscall_api_compatible(src_syscall, guest_api): 177 print(' // incompatible prototype') 178 print(' return RunGuestSyscall_%s(' % (name) + ', '.join(src_args) + ');') 179 continue 180 181 # Translate to syscall with the same entry. 182 if src_syscall['entry'] != dst_syscall['entry']: 183 print(' // %s on %s but %s on %s' % (src_syscall['entry'], src_arch, dst_syscall['entry'], dst_arch)) 184 print(' return RunGuestSyscall_%s(' % (name) + ', '.join(src_args) + ');') 185 continue 186 187 # Translate! 188 print(' return syscall(' + ', '.join([dst_syscall['id']] + dst_args) + ');') 189 190 print("""\ 191 default: 192 return RunUnknownGuestSyscall(guest_nr, arg_1, arg_2, arg_3, arg_4, arg_5, arg_6); 193 } 194}""") 195 196 197def main(argv): 198 if (len(argv) != 3): 199 print("Usage: " + argv[0] +" <src_arch> <dst_arch>") 200 return 1 201 202 workdir = os.path.dirname(argv[0]) 203 204 src_arch = argv[1] 205 dst_arch = argv[2] 206 207 with open(os.path.join(workdir, 'kernel_api_%s.json' % (src_arch))) as json_file: 208 guest_api = json.load(json_file) 209 210 with open(os.path.join(workdir, 'kernel_api_%s.json' % (dst_arch))) as json_file: 211 host_api = json.load(json_file) 212 213 api_analysis.mark_incompatible_api(guest_api, host_api, False) 214 215 with open(os.path.join(workdir, 'kernel_syscalls.json')) as json_file: 216 all_syscalls = json.load(json_file) 217 218 with open(os.path.join(workdir, 'custom_syscalls.json')) as json_file: 219 custom_syscalls = json.load(json_file) 220 221 _merge_syscalls(all_syscalls, custom_syscalls) 222 223 _print_syscalls_translation(src_arch, dst_arch, all_syscalls, guest_api) 224 225 return 0 226 227 228if __name__ == '__main__': 229 sys.exit(main(sys.argv)) 230