* ========== Copyright Header Begin ==========================================
* Hypervisor Software File: hcall.s
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* - Do no alter or remove copyright notices
* - Redistribution and use of this software in source and binary forms, with
* or without modification, are permitted provided that the following
* - Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of Sun Microsystems, Inc. or the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* This software is provided "AS IS," without a warranty of any kind.
* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN
* OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
* FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or maintenance of
* ========== Copyright Header End ============================================
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
.ident "@(#)hcall.s 1.99 07/05/03 SMI"
#include <sys/asm_linkage.h>
* hcall_core - Entry point for CORE_TRAP hcalls
* These calls are unversioned, and universal to all guests. They
* represent key functionality that a guest must have available
* even if API versions have not been negotiated.
* Calling conventions are identical to the FAST_TRAP conventions
* described for hcall, below.
cmp %o5, (.core_end - .core_table) / 4 ! in table?
bgeu,pn %xcc, herr_badtrap ! no, error
sllx %o5, 2, %g2 ! scaled index
LABEL_ADDRESS(.core_table, %g1) ! &core_table
jmp %g1 + %g2 ! ... and go!
ba,a,pt %xcc, hcall_api_set_version ! 0x00
ba,a,pt %xcc, hcall_cons_putchar ! 0x01
ba,a,pt %xcc, hcall_mach_exit ! 0x02
ba,a,pt %xcc, hcall_api_get_version ! 0x03
* hcall - Entry point for FAST_TRAP hcalls
* function# (%o5) - number of the specific API function to be invoked
* arg0-arg4 (%o0-%o4) arguments to the function
* ret0 (%o0) status (EOK, or an error code)
* ret1-ret5 (%o1-%o5) return values
* This code has access to fresh g-registers for scratch. %o5 is
* also legal for scratch, but the calling conventions require all
* other o-registers to be preserved unless the specific call uses
* the register either as an input or output argument.
ldx [%g2 + GUEST_HCALL_TABLE], %g2
cmp %o5, MAX_FAST_TRAP_VALUE
sllx %o5, API_ENTRY_SIZE_SHIFT, %g1
sub %o5, MAX_FAST_TRAP_VALUE, %g1
sllx %g1, API_ENTRY_SIZE_SHIFT, %g1
* api_set_version - select API version
* arg1 (%o1) major_version
* arg2 (%o2) minor_version
* ret1 (%o1) actual_minor
ENTRY_NP(hcall_api_set_version)
add %g2, GUEST_API_GROUPS, %g2 ! %g2 = &guest->api_groups
* API_GROUP_SUN4V is special. There are no API calls
* associated with this group. Instead, each major version
* in the api_group corresponds to a set of known CPU errata
* that the guest must work around. The meaning of minor
* numbers other than zero isn't defined; we're explicitly
* ignoring the passed in minor version.
* Note we store the major and minor numbers for
* API_GROUP_SUN4V for the api_get_version call, same as for
* any other API group. The index is the first entry in the
* guest's local info table.
cmp %o0, API_GROUP_SUN4V ! check for the special group
bne,pt %xcc, 0f ! not special, skip it
cmp %o1, SUN4V_VERSION_INITIAL ! check if supported
bne,pt %xcc, herr_notsupported ! unknown major
ba,pt %xcc, .storeversion ! return success
mov 0, %o2 ! ... with minor number 0
* Look up the table entry for the guest's requested
* We're calculating addresses in two tables:
* hcall_api_group_map (store in %g1), and the api_groups
* table in the guest structure (store in %g2).
setx hcall_api_group_map, %g3, %g1 ! %g1 = api mapping table
ROOT_STRUCT(%g3) ! address of config struct
ldx [%g3 + CONFIG_RELOC], %g3 ! ... for relocation offset
sub %g1, %g3, %g1 ! relocated table address
lduw [%g1+4], %g4 ! offset to next group entry
lduw [%g1], %g3 ! api group number
inc VERSION_SIZE, %g2 ! next api_groups entry
brz %g4, herr_inval ! EINVAL if end of table
cmp %g3, %o0 ! is this the one?
bne,a,pt %xcc, 0b ! ... no, keep looking
add %g1, %g4, %g1 ! next API group entry
* Register usage at this point:
* %o0-%o2 - HCALL arguments
* %g1 - pointer to the api_group entry in the mapping
* %g2 - pointer to the entry in guest->api_groups
* We have the information for the requested api_group. Our
* next step is to scan this api_group's entry to see if the
* requested major version is supported. If it is, check
* the maximum minor version we can handle, and if necessary
* adjust the guest's request.
* As a special case, if the requested major_version is 0,
* we disable the entire API group. There are checks here
* and in other places below. The checks aren't optional,
* because the VERSION_PTR for the API group isn't valid in
* this case. It's a bit hairy, so stay sharp out there.
brnz,pt %o1, .findmajor ! must search if major_version != 0
inc 8, %g1 ! advance to version info
! major_version == 0 means disable the api_group
mov 0, %g1 ! ... to be stored in guest struct
ba,pt %xcc, .check_disable
mov 0, %o2 ! ... to be stored in guest struct
! (and returned to guest)
brz,pn %g3, herr_notsupported ! not found, ENOTSUPPORTED
inc 3, %g4 ! version plus first two xwords
sllx %g4, 3, %g4 ! scale index
add %g1, %g4, %g1 ! skip over minor version data
lduw [%g1 + MAJOR_OFF], %g3 ! get major number from table
cmp %g3, %o1 ! is it a match?
bne,pt %xcc, 0b ! no, keep looking
lduw [%g1 + MINOR_OFF], %g4 ! get minor number from table
! Found the requested major number; check the requested
cmp %g4, %o2 ! minor number supported?
movlu %xcc, %g4, %o2 ! no, downgrade the request
inc 8, %g1 ! advance to minor version list
* Register usage at this point:
* %o0-%o2 - HCALL arguments
* %g1 - pointer to the list of minor version table
* %g2 - pointer to the entry in guest->api_groups
* We've found the info for the major number being requested
* Next big step, figure out if the guest's request is going
* to disable or enable any API functions.
* A picture to help explain the ubiquitous +1 found in all
* the index calculations below:
* +--------------------------------------------+
* 1.0 -> | ... 1.0 entries here ... |
* +--------------------------------------------+
* | ... post 1.0 entries here ... |
* +--------------------------------------------+
* 1.old-> | ... 1.old entries here are in use ... |
* +--------------------------------------------+
* 1.old+1-> | ... from here on must be enabled ... |
* +--------------------------------------------+
* | ... after 1.old, before 1.new ... |
* +--------------------------------------------+
* 1.new-> | ... 1.new entries here must be enabled ... |
* +--------------------------------------------+
* 1.new+1-> | ... stop enabling from here on ... |
* +--------------------------------------------+
* This picture applies to the case where the major number
* isn't changing, and the minor number is increasing.
* Similar pictures apply to the other cases; drawing them
* is left as an exercise for the reader.
lduw [%g2 + VERSION_MAJOR], %g3 ! old major number
cmp %g3, %o1 ! changing major numbers?
be,pt %xcc, .check_minor ! no, next check
lduw [%g2 + VERSION_MINOR], %g5 ! guest's old minor number
! We're changing major numbers, disable everything in the
brz,pt %g3, .check_enable ! nothing to disable if was 0.0
ldx [%g2 + VERSION_PTR], %g4 ! guest's old table entry
ldx [%g4], %g3 ! disable start addr
sllx %g5, 3, %g5 ! ... scaled
ldx [%g4 + %g5], %g4 ! disable end addr
! We're not changing the major number; if the major
! number was zero, we're done.
! Otherwise, check whether the guest is changing its minor
be,pn %xcc, .storeversion ! no, we're done
add %o2, 1, %g6 ! new_minor+1
bgu,pn %xcc, 0f ! old > new, downgrading
! We're upgrading from a lower minor number to a higher one.
ldx [%g1 + %g5], %g3 ! enable from old_minor+1
ldx [%g1 + %g6], %g4 ! ... to new_minor+1
! We're downgrading from a higher minor number to a lower
ldx [%g1 + %g6], %g3 ! disable from new_minor+1
ldx [%g1 + %g5], %g4 ! ... to old_minor+1
* Register usage at this point:
* %o0-%o2 - HCALL arguments
* %g1 - pointer to the list of minor version table
* %g2 - pointer to the entry in guest->api_groups
* %g3 - starting address of list of hcall functions to be
* %g4 - ending address of list of hcall functions to be
* Disable the entries indicated by the starting and ending
* addresses in %g3 and %g4.
dec HCALL_ENTRY_SIZE - HCALL_ENTRY_INDEX, %g4
sub %g3, %g4, %g3 ! adjust for loop check
LABEL_ADDRESS(herr_badtrap, %g5)
ldx [%g7 + GUEST_HCALL_TABLE], %g7 ! hcall table address
ldx [%g6 + CONFIG_RELOC], %g6
sub %g4, %g6, %g4 ! relocate end address
ldx [%g3 + %g4], %g6 ! function index
UPDATE_HCALL_TARGET(%g7, %g6, %g5)
inc HCALL_ENTRY_SIZE, %g3
* Register usage at this point:
* %o0-%o2 - HCALL arguments
* %g1 - pointer to the list of minor version table
* %g2 - pointer to the entry in guest->api_groups
* We've finished disabling any calls that won't be
* available. If the new major version is 0, then we're done.
brz,pn %o1, .storeversion ! done if major_version==0
sllx %g5, 3, %g5 ! (minor_version+1)*8
ldx [%g1], %g3 ! enable start addr
ldx [%g1 + %g5], %g4 ! enable end addr
* Register usage at this point:
* %o0-%o2 - HCALL arguments
* %g1 - pointer to the list of minor version table
* %g2 - pointer to the entry in guest->api_groups
* %g3 - starting address of list of hcall functions to be
* %g4 - ending address of list of hcall functions to be
* Enable the entries indicated by the starting and ending
* addresses in %g3 and %g4.
dec HCALL_ENTRY_SIZE - HCALL_ENTRY_INDEX, %g4
ldx [%g7 + GUEST_HCALL_TABLE], %g7 ! hcall table address
ldx [%g6 + CONFIG_RELOC], %g6
sub %g3, %g6, %g3 ! relocate start address
sub %g4, %g6, %g4 ! relocate end address
ldx [%g3 + HCALL_ENTRY_INDEX], %g6 ! function index
ldx [%g3 + HCALL_ENTRY_LABEL], %g5 ! target address
ldx [%o0 + CONFIG_RELOC], %o0
sub %g5, %o0, %g5 ! relocated target
UPDATE_HCALL_TARGET(%g7, %g6, %g5)
inc HCALL_ENTRY_SIZE, %g3
sllx %o1, MAJOR_SHIFT, %g3
stx %g3, [%g2 + VERSION_NUM]
stx %g1, [%g2 + VERSION_PTR]
SET_SIZE(hcall_api_set_version)
* api_get_version - select API version
* reg1 (%o1) major_version
* reg2 (%o2) minor_version
ENTRY_NP(hcall_api_get_version)
add %g2, GUEST_API_GROUPS, %g2 ! %g2 = guest's local table
* Check for API_GROUP_SUN4V. This API group number isn't
* in the mapping table; the version info for this API group
* is the first entry in the guest's local info table.
cmp %o0, API_GROUP_SUN4V ! check for the special group
be,pn %xcc, .getversion ! special, we have the address
* Look up the table entry for the guest's requested
* There are two tables: the global table that maps API
* groups onto available API functions, and the guest's
* local table that indicates what version the guest has
* selected for each API group.
setx hcall_api_group_map, %g3, %g1 ! %g1 = api mapping table
ROOT_STRUCT(%g3) ! address of config struct
ldx [%g3 + CONFIG_RELOC], %g3 ! ... for relocation offset
sub %g1, %g3, %g1 ! relocated table address
lduw [%g1+4], %g4 ! offset to next group entry
lduw [%g1], %g3 ! api group number
inc VERSION_SIZE, %g2 ! next api_groups entry
brz %g4, herr_inval ! EINVAL if end of table
cmp %g3, %o0 ! is this the one?
bne,a,pt %xcc, 0b ! ... no, keep looking
add %g1, %g4, %g1 ! next API group entry
ldx [%g2 + VERSION_NUM], %g3
srlx %g3, MAJOR_SHIFT, %o1
sllx %g3, 64-MAJOR_SHIFT, %g3
srlx %g3, 64-MAJOR_SHIFT, %o2
SET_SIZE(hcall_api_get_version)
* Common error escapes so errors can be implemented by
SET_SIZE(herr_wouldblock)
SET_SIZE(herr_notsupported)
#ifdef CONFIG_VERSION_TEST
* Test API calls to go with test API group 0x400 above.
\ Cut and paste this at ok prompt if you want to test
2 3 0 7f hypercall: api-set-version
3 1 3 7f hypercall: api-get-version
1 0 4 7f hypercall: bad-core4
1 0 -1 7f hypercall: bad-core-1
2 0 e0 0 hypercall: version-e0
2 0 e1 0 hypercall: version-e1
2 0 e2 0 hypercall: version-e2
2 0 e3 0 hypercall: version-e3
: test-api ( mjr mnr -- )
swap 400 api-set-version ." set-version: " . . cr
400 api-get-version ." get-version: " . . . cr
version-e0 ." e0: " . . cr
version-e1 ." e1: " . . cr
version-e2 ." e2: " . . cr
version-e3 ." e3: " . . cr
ENTRY(hcall_version_test_1_0)
SET_SIZE(hcall_version_test_1_0)
ENTRY(hcall_version_test_1_1)
SET_SIZE(hcall_version_test_1_1)
ENTRY(hcall_version_test_1_2)
SET_SIZE(hcall_version_test_1_2)
ENTRY(hcall_version_test_2_0)
SET_SIZE(hcall_version_test_2_0)
ENTRY(hcall_version_test_2_1)
SET_SIZE(hcall_version_test_2_1)
ENTRY(hcall_version_test_2_2)
SET_SIZE(hcall_version_test_2_2)
ENTRY(hcall_version_test_3_0)
SET_SIZE(hcall_version_test_3_0)