From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752116AbdISUyJ (ORCPT ); Tue, 19 Sep 2017 16:54:09 -0400 Received: from mail-by2nam01on0052.outbound.protection.outlook.com ([104.47.34.52]:32170 "EHLO NAM01-BY2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751840AbdISUrL (ORCPT ); Tue, 19 Sep 2017 16:47:11 -0400 Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=brijesh.singh@amd.com; From: Brijesh Singh To: linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: Brijesh Singh , Thomas Gleixner , Ingo Molnar , "H. Peter Anvin" , Paolo Bonzini , =?UTF-8?q?Radim=20Kr=C4=8Dm=C3=A1=C5=99?= , Joerg Roedel , Borislav Petkov , Tom Lendacky , x86@kernel.org Subject: [Part2 PATCH v4 14/29] KVM: SVM: Add KVM_SEV_INIT command Date: Tue, 19 Sep 2017 15:46:12 -0500 Message-Id: <20170919204627.3875-15-brijesh.singh@amd.com> X-Mailer: git-send-email 2.9.5 In-Reply-To: <20170919204627.3875-1-brijesh.singh@amd.com> References: <20170919204627.3875-1-brijesh.singh@amd.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Originating-IP: [165.204.78.1] X-ClientProxiedBy: CY4PR04CA0071.namprd04.prod.outlook.com (10.171.243.164) To SN1PR12MB0158.namprd12.prod.outlook.com (10.162.3.145) X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 84df0137-ac07-4543-df38-08d4ff9f94d2 X-MS-Office365-Filtering-HT: Tenant X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:(300000500095)(300135000095)(300000501095)(300135300095)(22001)(300000502095)(300135100095)(2017030254152)(48565401081)(300000503095)(300135400095)(2017052603199)(201703131423075)(201703031133081)(201702281549075)(300000504095)(300135200095)(300000505095)(300135600095)(300000506095)(300135500095);SRVR:SN1PR12MB0158; X-Microsoft-Exchange-Diagnostics: 1;SN1PR12MB0158;3:YRSNPjJMnNSlX8gvyr4y+BqtXQdyMrJsr/riv1EEGaEUWSEw3nk47ukOvwACtSjkqlOuqG/E2LMjh+DPB2xLMXWpM+RdtRdhxtp18VyiSw9QLYVIaqfK3/jFSe/KJBZaKDHUNhyVZ+aaR1LpnU/ddMPo8kR99a0cF/ow7ubGnEwLeRa1wH2cg6Bce6IE7pORU+XXDttjvrNNioVFLCnR3VwaXDeA5JZeyOOtdL/kT6n+g88+3mzBu+7IT3f9H62E;25:kstsll2tiZFOs4ttc89ZSxtfbau1u6KeP8Pv75KRcKMQC1OFo2OO4T9XCrMPckIB6HEs9FCwZr+QkGWn8CUBvSatYX7+RsoA6JphtSgBu2NdpGcJyUU1sylZWZDfGqrTMtNFXWNaAKz6TWClV6y/S1v4iMFhCh6aIiOTJGz9j2/LQFzFAS2C0JmXImfoSpMaiQEmT/zt/lrVcCqyFB/fG8ZdOdDePgTxywqmOdsOYjdl62y4xcCskYgAs6vRm53VIb+Xb8yDQvmLuOtS5rB+hsyFqcgY37UjP7MzUidO/3woU+QSYxiKX5d2Q0qbqWWYuXtOmCZratyivLg/zmE7fw==;31:36RJ74Ihp8RJXML6Jlrw5vKs5VlyuaPk3mDjLoOlt38wNV2YrnVPBy4Xl/x4vbmYw7658/PB+g2fxhkYMr6OT5JIBmm4i873bCpByxi2qOd3TKBTYIGUmUrpeFmKJJ4cEDFL58xDLkdUeDIFE3Fj69yxQ0rtUw1/PAFJBucOq0z1Is4hpxPq5NZ6fIjKR0R38G1JpAFlhLrUY4KhUiDFnkE7kXQ/hQhJUYUZ3YkZ1Qk= X-MS-TrafficTypeDiagnostic: SN1PR12MB0158: X-Microsoft-Exchange-Diagnostics: 1;SN1PR12MB0158;20:Uoar1OQu9Dy++th4FSIMmISB5y1cWqAYspnEaee6wyrAM/YswGZ4zYkpElLB9sWqjPY+MQJB1YuD3KoXe3lavRq/vjMyhUI9149AAwG7mV/1PU5J2wmg4H2Tp35v0iNehmpfQ4emIBUtpHaWOfGmOy742uT8gwjVcwwwMCILbJ7MHTtNIVZtqgJSN9EKjHX50vaRv/wkyh9XQD/Azb2D7DmMkXFL+G0htaM/3a2EHys8CwZCGl0gKMrlWQ3IW7PMVO+2RWp+vA5V/bFhqB0oYXQWb1/p6p+WOxBaRVAJ3pa5VVd2gXw9P/XtzGo6+9jwUFZEdjOJDGLSMPuSa2uVnk/kD8jaBETcCqb6JSJTuamdOogqz7pn++B9cdIgQn/BpiaaYG3V3peQLp2JDlgKxGFgh4euOTeI4c4Pb85s1rER44h6hKsVY6n5R/l13/0fk3jMlc22IZU+5O3Wz/fycG405tiwMFsK0IzMccmnl735kNups/47ORYdtESD6GH0;4:jBeoQfKP+ct9pLH0XkR6UoxTKHJ3h8jmKii663ePDmYbAESU63p6XMxmcrNejSUSeHf+tCTMInHtRlGmQJfLOOyc/Q7xEw6JuTI3MQPxSEM7oAxIYBBfb2s9jfUGvyn3ILdKgT8j856L4+XO5mDqo8DFsFCE80C7Uans9iz1vmz+vupYHNY5WpfwxJbjfL8Mgev8xgnL1rbaf1nzL8iDf3X+ZY32nirHhvl79zMQ+9KO76jiHnLOHy4vYf/0c2HalgeY9lCgMnhvoT/HDo8nK3+fcKaR0vTOBUFXQqJqWXr80Nn87S87FMqIpKsxitLPayZmitUDxPRNLNelfkie2A== X-Exchange-Antispam-Report-Test: UriScan:(9452136761055)(767451399110); X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(100000700101)(100105000095)(100000701101)(100105300095)(100000702101)(100105100095)(6040450)(2401047)(8121501046)(5005006)(93006095)(93001095)(100000703101)(100105400095)(3002001)(10201501046)(6055026)(6041248)(20161123560025)(20161123558100)(201703131423075)(201702281528075)(201703061421075)(201703061406153)(20161123555025)(20161123562025)(20161123564025)(6072148)(201708071742011)(100000704101)(100105200095)(100000705101)(100105500095);SRVR:SN1PR12MB0158;BCL:0;PCL:0;RULEID:(100000800101)(100110000095)(100000801101)(100110300095)(100000802101)(100110100095)(100000803101)(100110400095)(100000804101)(100110200095)(100000805101)(100110500095);SRVR:SN1PR12MB0158; X-Forefront-PRVS: 04359FAD81 X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10009020)(6009001)(346002)(376002)(39860400002)(199003)(189002)(86362001)(50466002)(8936002)(97736004)(6116002)(3846002)(7416002)(81156014)(8676002)(50226002)(2950100002)(81166006)(316002)(478600001)(16526017)(6666003)(7736002)(66066001)(305945005)(47776003)(6486002)(189998001)(53416004)(25786009)(101416001)(2870700001)(50986999)(76176999)(53936002)(2906002)(68736007)(36756003)(106356001)(1076002)(4326008)(5660300001)(105586002)(33646002)(23676002)(54906003);DIR:OUT;SFP:1101;SCL:1;SRVR:SN1PR12MB0158;H:ubuntu-010236106000.amd.com;FPR:;SPF:None;PTR:InfoNoRecords;A:1;MX:1;LANG:en; X-Microsoft-Exchange-Diagnostics: =?utf-8?B?MTtTTjFQUjEyTUIwMTU4OzIzOi9LT3hxdHlKektLMHJyVFpkQVlXUFZsa3JY?= =?utf-8?B?TDNycS9uS0RTaGJWbWZ3b1pOWTMyTmo4NlRJTXBBSHh4M00vRWEyTFMwY1ZP?= =?utf-8?B?emJxRHRQcDVVWldhamtENnFEVkZGdDZhR0F0UURSQnQwV0x0THdjVXYrejda?= =?utf-8?B?ZDlFYTJLSElmUkZwSTdtM3pFa1FNSDE4QXVXekg0MTZibWJLREJ3b25mTyto?= =?utf-8?B?b2c2Q1lRcWtFamhrK2QxZDhmajBnVm11cmlFV2RvOHliVnlpbzJPWjlCaGZa?= =?utf-8?B?THA3UVBjOHNmMlZ6WDlFUFlsTXFMQXhqc1lYSm1xdm43TmtvMU10MVFybExp?= =?utf-8?B?MzhOWFBNZVV5S1ppMU9xRzVORVVRN1NtUjNvMU1iRHJCTUt5cUkxdmtMa2pK?= =?utf-8?B?ajZOSnYrQ2llT08wekVBWFNoYTdxc1o4c3JQRU1JQzViNDRBeGFGbElmcDdL?= =?utf-8?B?cVNzM080VHN6UmZjR2dxN3IzK2l4QVp5aHlOajlzdllhc1l2aGFXL3R2TFBs?= =?utf-8?B?WkZmNkRFbW13WDFHZGEwbzcyR0hhdnZrMHhFM0xsd1lTS1NBMlpoRkhET29O?= =?utf-8?B?K0ZuclRvcVRBeDNybGw0UXV6TDkwd2dQMElRdXdHbnI0bDUyd1d3NldZN1pi?= =?utf-8?B?OGd0QjdYT2ZMVllFOHdyQUp5MUdmbjgyMmJmOEJEaVMrMkordXlDS0hYTDhU?= =?utf-8?B?K1lYM2hzbFZqeS9xMFViS1UvZDl2TXRrQ0EwTzdmVnYram1UTGx6VVNTUXRW?= =?utf-8?B?cGQ2MHpCeU9kSU8zN3MrRXZRK3pTbmlFZ2pHdEJlWlhTYnU4TU5LZk5wekJW?= =?utf-8?B?MjF5ZCtpR3RqYkJ2Vm85Q3V1SzVhVWIxS3RhM1NYeU1OSkZueElOMHM4ejZy?= =?utf-8?B?YnAzc1ZrQUZ2bi9tSDljbkpGYnlwM2hNaUdNSUVTcTExMnZJOHloYmJ0a29y?= =?utf-8?B?NTFYVzNXNWFIRkhURmZudjF1OW9lOWFkaDJ5WmNObTdlTkRkYXFzbnJJTURr?= =?utf-8?B?SzlYMTlkcW9aV0tCRzRvZEIwd0ZUWDhpVEZhRkFLRjVEOS9aY00wOUQxUmNl?= =?utf-8?B?aVFXM3U4RFIvZ3d2UVpPdnlFOG85YzhscGxuaENvd2QrYmxhR3RHT1RFZ0lr?= =?utf-8?B?WUV1ZWNKU3JlRFhtMGYvcjJycCt5bFhuV0YzUjVhVFV4R20xbFJ4cW50MVRa?= =?utf-8?B?M1dsekVDejIyVzVTdzQzSTIxeEJCUzV3VjMzTHVGc1I2Q1Y1Uzl3YkQrblZm?= =?utf-8?B?L2R3OWxsd3pyaElORndWOVh6QVZpcTVuVmU3Qi9BTC8rc3gwUzRvRlI2REwr?= =?utf-8?B?cDJyeTkrdGdKTllENEtSUFpmYVYzMWliSytWZytuSmlkdk85RzM2WDRQSEI0?= =?utf-8?B?bDBlSUsxTlhKU0ZhMTR0VThjeHZ2QUpubDJzcUJMTE9JVHJjcFhaT2hZc3Nr?= =?utf-8?Q?7C0ZfE4rbQNWT/eXKPwVuH3xGFu?= X-Microsoft-Exchange-Diagnostics: 1;SN1PR12MB0158;6:bjDqQEjCqkPTS2dw7ZvuXMdSEi7jRXYzEJ/ZM1aGzErhTPF5plFZDcWvUIGy5XSqgHgk5bUa5rxdckMXP0cL4vMG7NLJ0t7AnyBpboQzvatW2OFaGmWV8kcHrIbCn2q+MXFQHGThUGnHL1PTiuQuX866Iw3oLF7rL/HN9r3RXLX/OONQMNEyQpOkJCCJJz69FO0rcOmjuFS2pg+P9oqP/13a0laKA9aqm2WTspRubTIQx+3xzvHzaykOPlYcIAcAk8FQcTfXjnaC0CMtso8pad2eNIWayeYM+PrKbwwJm+85Ar1xXAqt8DulazMKcoMldFFLeayJOuaD/QvKUVKEew==;5:OzefTfuwZdn3H8nYMzBYxwmwZarulJAaIbvKNQEMjgzpGFpv29p69ezZLB4zlCPBIi8/Zo1YL2bAVSJIVCxQMx6nXoQuFpYZl/QR6wl83nV4RiVqD4Y8L5OCKcdmda8ANmZiw6IZvvUS2m4M0TYZiQ==;24:hWNWY7Vy3ydBd65lvugYkV0IXej88zDXAnls6x5ZDXzONg9W2b9Qy+zWoYDX9YqY2dcDvaDAbKvLC3W28P0+1z3xnyC0s+E99E2ormC6Lyg=;7:Uheicb7DXa78lZIyrTQi6f2ztQjkCxQxUhwrtCn2Nh4R7h/zF+OS4zu0zyWHf676Saooxh8qedH3nv0MWRwohNFaopbddVXSFDLusQkwyOdBFc6AHfjhwlPpEceB9CbIs8/HpC+YF9TDOqIjjbTvV5icdnqACUH8dX2oSXEe74biw4xo1asWfwcIjU99jAvGoC9gBg/kX+TKUSr6cxJ6+yItFLDKZEe2EGm3H+77VHg= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1;SN1PR12MB0158;20:IsBR9xQRpxcXZC20ynBIbOOzSyzV1yJTRcn+RNWCQndJci3An1AohWgw5HNi9KT9XJLafYUC7AN9tsf6uUJ75AxwnYCzs8hbwTn7bbOUzMQ96/T5JTJCJ3Dfc2DkXVmChTstzNDIXZSivWmF2zxGcsnFmQSdTm3olDJATr/4cU6uIZuL32vHlJbEybbdESWkg01kXLdRJ8JnXq962ErUGtu54iOHdBlJ1dY9tc5Otb8Eqr4HxfrOYop7bhuKbkG3 X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 19 Sep 2017 20:47:01.8908 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN1PR12MB0158 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The command initializes the SEV firmware and allocates a new ASID for this guest from the SEV ASID pool. The firmware must be initialized before we issue any guest launch commands to create a new memory encryption context. Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Paolo Bonzini Cc: "Radim Krčmář" Cc: Joerg Roedel Cc: Borislav Petkov Cc: Tom Lendacky Cc: x86@kernel.org Cc: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Brijesh Singh --- arch/x86/include/asm/kvm_host.h | 7 ++ arch/x86/kvm/svm.c | 184 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 190 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 20fba8bfa727..6bd49e80a16d 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -747,6 +747,11 @@ enum kvm_irqchip_mode { KVM_IRQCHIP_SPLIT, /* created with KVM_CAP_SPLIT_IRQCHIP */ }; +struct kvm_sev_info { + bool active; /* SEV enabled guest */ + unsigned int asid; /* ASID used for this guest */ +}; + struct kvm_arch { unsigned int n_used_mmu_pages; unsigned int n_requested_mmu_pages; @@ -834,6 +839,8 @@ struct kvm_arch { bool x2apic_format; bool x2apic_broadcast_quirk_disabled; + + struct kvm_sev_info sev_info; }; struct kvm_vm_stat { diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 3535aa510194..229bb7d09c44 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include #include @@ -320,6 +322,19 @@ enum { #define VMCB_AVIC_APIC_BAR_MASK 0xFFFFFFFFFF000ULL static unsigned int max_sev_asid; +static unsigned long *sev_asid_bitmap; + +static inline bool svm_sev_enabled(void) +{ + return max_sev_asid; +} + +static inline bool sev_guest(struct kvm *kvm) +{ + struct kvm_sev_info *sev = &kvm->arch.sev_info; + + return sev->active; +} static inline void mark_all_dirty(struct vmcb *vmcb) { @@ -1059,6 +1074,11 @@ static int avic_ga_log_notifier(u32 ga_tag) static __init void sev_hardware_setup(void) { max_sev_asid = cpuid_ecx(0x8000001F); + + /* Initialize SEV ASID bitmap */ + if (max_sev_asid) + sev_asid_bitmap = kcalloc(BITS_TO_LONGS(max_sev_asid), + sizeof(unsigned long), GFP_KERNEL); } static __init int svm_hardware_setup(void) @@ -1156,10 +1176,45 @@ static __init int svm_hardware_setup(void) return r; } +static int sev_platform_get_state(int *state, int *error) +{ + int ret; + struct sev_data_status *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = sev_platform_status(data, error); + if (!ret) + *state = data->state; + + pr_info_once("SEV firmware major %d minor %d build %d\n", + data->api_major, data->api_minor, data->build); + + kfree(data); + return ret; +} + +static __exit void sev_hardware_unsetup(void) +{ + int state; + + sev_platform_get_state(&state, NULL); + + if (state != SEV_STATE_UNINIT) + sev_platform_shutdown(NULL); + + kfree(sev_asid_bitmap); +} + static __exit void svm_hardware_unsetup(void) { int cpu; + if (svm_sev_enabled()) + sev_hardware_unsetup(); + for_each_possible_cpu(cpu) svm_cpu_uninit(cpu); @@ -1350,6 +1405,9 @@ static void init_vmcb(struct vcpu_svm *svm) svm->vmcb->control.int_ctl |= V_GIF_ENABLE_MASK; } + if (sev_guest(svm->vcpu.kvm)) + svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ENABLE; + mark_all_dirty(svm->vmcb); enable_gif(svm); @@ -1432,6 +1490,35 @@ static int avic_init_backing_page(struct kvm_vcpu *vcpu) return 0; } +static void sev_firmware_exit(void) +{ + int state; + + sev_platform_get_state(&state, NULL); + + if (state == SEV_STATE_INIT) + sev_platform_shutdown(NULL); +} + +static void sev_asid_free(struct kvm *kvm) +{ + struct kvm_sev_info *sev = &kvm->arch.sev_info; + int pos, asid; + + if (svm_sev_enabled()) + return; + + asid = sev->asid; + pos = asid - 1; + clear_bit(pos, sev_asid_bitmap); +} + +static void sev_vm_destroy(struct kvm *kvm) +{ + sev_asid_free(kvm); + sev_firmware_exit(); +} + static void avic_vm_destroy(struct kvm *kvm) { unsigned long flags; @@ -1450,6 +1537,14 @@ static void avic_vm_destroy(struct kvm *kvm) spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags); } +static void svm_vm_destroy(struct kvm *kvm) +{ + avic_vm_destroy(kvm); + + if (sev_guest(kvm)) + sev_vm_destroy(kvm); +} + static int avic_vm_init(struct kvm *kvm) { unsigned long flags; @@ -5416,6 +5511,91 @@ static void svm_setup_mce(struct kvm_vcpu *vcpu) vcpu->arch.mcg_cap &= 0x1ff; } +static int sev_asid_new(void) +{ + int pos; + + if (!svm_sev_enabled()) + return -EINVAL; + + pos = find_first_zero_bit(sev_asid_bitmap, max_sev_asid); + if (pos >= max_sev_asid) + return -EBUSY; + + set_bit(pos, sev_asid_bitmap); + return pos + 1; +} + +static int sev_firmware_init(struct kvm *kvm, int *error) +{ + int ret, state; + + ret = sev_platform_get_state(&state, error); + if (ret) + return ret; + + /* If SEV firmware is in uninitialized state, lets initialize it. */ + if (state == SEV_STATE_UNINIT) { + struct sev_data_init *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = sev_platform_init(data, error); + kfree(data); + } + + return ret; +} + +static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp) +{ + struct kvm_sev_info *sev = &kvm->arch.sev_info; + int asid, ret; + + ret = sev_firmware_init(kvm, &argp->error); + if (ret) + return ret; + + ret = -EBUSY; + asid = sev_asid_new(); + if (asid < 0) + goto e_shutdown; + + sev->active = true; + sev->asid = asid; + return 0; +e_shutdown: + sev_firmware_exit(); + return ret; +} + +static int svm_mem_enc_op(struct kvm *kvm, void __user *argp) +{ + struct kvm_sev_cmd sev_cmd; + int r = -ENOTTY; + + if (copy_from_user(&sev_cmd, argp, sizeof(struct kvm_sev_cmd))) + return -EFAULT; + + mutex_lock(&kvm->lock); + + switch (sev_cmd.id) { + case KVM_SEV_INIT: { + r = sev_guest_init(kvm, &sev_cmd); + break; + } + default: + break; + } + + mutex_unlock(&kvm->lock); + if (copy_to_user(argp, &sev_cmd, sizeof(struct kvm_sev_cmd))) + r = -EFAULT; + return r; +} + static struct kvm_x86_ops svm_x86_ops __ro_after_init = { .cpu_has_kvm_support = has_svm, .disabled_by_bios = is_disabled, @@ -5432,7 +5612,7 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = { .vcpu_reset = svm_vcpu_reset, .vm_init = avic_vm_init, - .vm_destroy = avic_vm_destroy, + .vm_destroy = svm_vm_destroy, .prepare_guest_switch = svm_prepare_guest_switch, .vcpu_load = svm_vcpu_load, @@ -5526,6 +5706,8 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = { .deliver_posted_interrupt = svm_deliver_avic_intr, .update_pi_irte = svm_update_pi_irte, .setup_mce = svm_setup_mce, + + .mem_enc_op = svm_mem_enc_op, }; static int __init svm_init(void) -- 2.9.5