Merge branch '2020-08-14-assorted-updates'
- Xen guest and some paravirt driver support. - Aspeed SoC updates - Broadcom IPROC PCIe RC driver
This commit is contained in:
commit
c0192950df
18
Kconfig
18
Kconfig
@ -105,6 +105,24 @@ config CC_COVERAGE
|
||||
config CC_HAS_ASM_INLINE
|
||||
def_bool $(success,echo 'void foo(void) { asm inline (""); }' | $(CC) -x c - -c -o /dev/null)
|
||||
|
||||
config XEN
|
||||
bool "Select U-Boot be run as a bootloader for XEN Virtual Machine"
|
||||
help
|
||||
Enabling this option will make U-Boot be run as a bootloader
|
||||
for XEN [1] Virtual Machine.
|
||||
|
||||
Xen is a virtual machine monitor (VMM) or a type-1 hypervisor with support
|
||||
for para-virtualization. Xen can organize the safe execution of several
|
||||
virtual machines on the same physical system with performance close to
|
||||
native. It is used as the basis for a number of different commercial and
|
||||
open source applications, such as: server virtualization, Infrastructure
|
||||
as a Service (IaaS), desktop virtualization, security applications,
|
||||
embedded and hardware appliances.
|
||||
Xen has a special VM called Domain-0 that runs the Dom0 kernel and allows
|
||||
Xen to use the device drivers for the Domain-0 kernel by default.
|
||||
|
||||
[1] - https://xenproject.org/
|
||||
|
||||
config DISTRO_DEFAULTS
|
||||
bool "Select defaults suitable for booting general purpose Linux distributions"
|
||||
select AUTO_COMPLETE
|
||||
|
@ -149,5 +149,6 @@ BSD 3-clause "New" or "Revised" License BSD-3-Clause Y bsd-3-clause.txt http:/
|
||||
IBM PIBS (PowerPC Initialization and IBM-pibs ibm-pibs.txt
|
||||
Boot Software) license
|
||||
ISC License ISC Y isc.txt https://spdx.org/licenses/ISC
|
||||
MIT License MIT Y mit.txt https://spdx.org/licenses/MIT.html
|
||||
SIL OPEN FONT LICENSE (OFL-1.1) OFL-1.1 Y OFL.txt https://spdx.org/licenses/OFL-1.1.html
|
||||
X11 License X11 x11.txt https://spdx.org/licenses/X11.html
|
||||
|
20
Licenses/mit.txt
Normal file
20
Licenses/mit.txt
Normal file
@ -0,0 +1,20 @@
|
||||
MIT License
|
||||
Copyright (c) 2020 EPAM Systems Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
12
MAINTAINERS
12
MAINTAINERS
@ -148,6 +148,18 @@ F: include/configs/meson64_android.h
|
||||
F: doc/board/amlogic/
|
||||
N: meson
|
||||
|
||||
ARM ASPEED
|
||||
M: Ryan Chen <ryan_chen@aspeedtech.com>
|
||||
M: Chia-Wei Wang <chiawei_wang@aspeedtech.com>
|
||||
R: Aspeed BMC SW team <BMC-SW@aspeedtech.com>
|
||||
S: Maintained
|
||||
F: arch/arm/mach-aspeed/
|
||||
F: arch/arm/include/asm/arch-aspeed/
|
||||
F: board/aspeed/
|
||||
F: drivers/clk/aspeed/
|
||||
F: drivers/pinctrl/aspeed/
|
||||
N: aspeed
|
||||
|
||||
ARM BROADCOM BCM283X
|
||||
M: Matthias Brugger <mbrugger@suse.com>
|
||||
S: Maintained
|
||||
|
@ -1749,6 +1749,14 @@ config TARGET_PRESIDIO_ASIC
|
||||
bool "Support Cortina Presidio ASIC Platform"
|
||||
select ARM64
|
||||
|
||||
config TARGET_XENGUEST_ARM64
|
||||
bool "Xen guest ARM64"
|
||||
select ARM64
|
||||
select XEN
|
||||
select OF_CONTROL
|
||||
select LINUX_KERNEL_IMAGE_HEADER
|
||||
select XEN_SERIAL
|
||||
select SSCANF
|
||||
endchoice
|
||||
|
||||
config ARCH_SUPPORT_TFABOOT
|
||||
@ -1955,6 +1963,7 @@ source "board/xilinx/Kconfig"
|
||||
source "board/xilinx/zynq/Kconfig"
|
||||
source "board/xilinx/zynqmp/Kconfig"
|
||||
source "board/phytium/durian/Kconfig"
|
||||
source "board/xen/xenguest_arm64/Kconfig"
|
||||
|
||||
source "arch/arm/Kconfig.debug"
|
||||
|
||||
|
@ -40,3 +40,4 @@ obj-$(CONFIG_TARGET_HIKEY) += hisilicon/
|
||||
obj-$(CONFIG_ARMV8_PSCI) += psci.o
|
||||
obj-$(CONFIG_ARCH_SUNXI) += lowlevel_init.o
|
||||
obj-$(CONFIG_TARGET_BCMNS3) += bcmns3/
|
||||
obj-$(CONFIG_XEN) += xen/
|
||||
|
6
arch/arm/cpu/armv8/xen/Makefile
Normal file
6
arch/arm/cpu/armv8/xen/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) 2018 NXP
|
||||
# (C) 2020 EPAM Systems Inc.
|
||||
|
||||
obj-y += lowlevel_init.o hypercall.o
|
79
arch/arm/cpu/armv8/xen/hypercall.S
Normal file
79
arch/arm/cpu/armv8/xen/hypercall.S
Normal file
@ -0,0 +1,79 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* hypercall.S
|
||||
*
|
||||
* Xen hypercall wrappers
|
||||
*
|
||||
* Stefano Stabellini <stefano.stabellini@eu.citrix.com>, Citrix, 2012
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation; or, when distributed
|
||||
* separately from the Linux kernel or incorporated into other
|
||||
* software packages, subject to the following license:
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this source file (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The Xen hypercall calling convention is very similar to the procedure
|
||||
* call standard for the ARM 64-bit architecture: the first parameter is
|
||||
* passed in x0, the second in x1, the third in x2, the fourth in x3 and
|
||||
* the fifth in x4.
|
||||
*
|
||||
* The hypercall number is passed in x16.
|
||||
*
|
||||
* The return value is in x0.
|
||||
*
|
||||
* The hvc ISS is required to be 0xEA1, that is the Xen specific ARM
|
||||
* hypercall tag.
|
||||
*
|
||||
* Parameter structs passed to hypercalls are laid out according to
|
||||
* the ARM 64-bit EABI standard.
|
||||
*/
|
||||
|
||||
#include <xen/interface/xen.h>
|
||||
|
||||
#define XEN_HYPERCALL_TAG 0xEA1
|
||||
|
||||
#define HYPERCALL_SIMPLE(hypercall) \
|
||||
.globl HYPERVISOR_##hypercall; \
|
||||
.align 4,0x90; \
|
||||
HYPERVISOR_##hypercall: \
|
||||
mov x16, #__HYPERVISOR_##hypercall; \
|
||||
hvc XEN_HYPERCALL_TAG; \
|
||||
ret; \
|
||||
|
||||
#define HYPERCALL0 HYPERCALL_SIMPLE
|
||||
#define HYPERCALL1 HYPERCALL_SIMPLE
|
||||
#define HYPERCALL2 HYPERCALL_SIMPLE
|
||||
#define HYPERCALL3 HYPERCALL_SIMPLE
|
||||
#define HYPERCALL4 HYPERCALL_SIMPLE
|
||||
#define HYPERCALL5 HYPERCALL_SIMPLE
|
||||
|
||||
.text
|
||||
|
||||
HYPERCALL2(xen_version);
|
||||
HYPERCALL3(console_io);
|
||||
HYPERCALL3(grant_table_op);
|
||||
HYPERCALL2(sched_op);
|
||||
HYPERCALL2(event_channel_op);
|
||||
HYPERCALL2(hvm_op);
|
||||
HYPERCALL2(memory_op);
|
||||
|
33
arch/arm/cpu/armv8/xen/lowlevel_init.S
Normal file
33
arch/arm/cpu/armv8/xen/lowlevel_init.S
Normal file
@ -0,0 +1,33 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* (C) 2017 NXP
|
||||
* (C) 2020 EPAM Systems Inc.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
.align 8
|
||||
.global rom_pointer
|
||||
rom_pointer:
|
||||
.space 32
|
||||
|
||||
/*
|
||||
* Routine: save_boot_params (called after reset from start.S)
|
||||
*/
|
||||
|
||||
.global save_boot_params
|
||||
save_boot_params:
|
||||
/* The firmware provided ATAG/FDT address can be found in r2/x0 */
|
||||
adr x1, rom_pointer
|
||||
stp x0, x2, [x1], #16
|
||||
stp x3, x4, [x1], #16
|
||||
|
||||
/* Returns */
|
||||
b save_boot_params_ret
|
||||
|
||||
.global restore_boot_params
|
||||
restore_boot_params:
|
||||
adr x1, rom_pointer
|
||||
ldp x0, x2, [x1], #16
|
||||
ldp x3, x4, [x1], #16
|
||||
ret
|
20
arch/arm/include/asm/arch-aspeed/platform.h
Normal file
20
arch/arm/include/asm/arch-aspeed/platform.h
Normal file
@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) ASPEED Technology Inc.
|
||||
* Ryan Chen <ryan_chen@aspeedtech.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ASM_ARCH_PLATFORM_H
|
||||
#define _ASM_ARCH_PLATFORM_H
|
||||
|
||||
#if defined(CONFIG_ASPEED_AST2500)
|
||||
#define ASPEED_MAC_COUNT 2
|
||||
#define ASPEED_DRAM_BASE 0x80000000
|
||||
#define ASPEED_SRAM_BASE 0x1e720000
|
||||
#define ASPEED_SRAM_SIZE 0x9000
|
||||
#else
|
||||
#err "Unrecognized Aspeed platform."
|
||||
#endif
|
||||
|
||||
#endif
|
@ -110,9 +110,13 @@ static inline void __raw_readsl(unsigned long addr, void *data, int longlen)
|
||||
* have some advantages to use them instead of the simple one here.
|
||||
*/
|
||||
#define mb() dsb()
|
||||
#define rmb() dsb()
|
||||
#define wmb() dsb()
|
||||
#define __iormb() dmb()
|
||||
#define __iowmb() dmb()
|
||||
|
||||
#define smp_processor_id() 0
|
||||
|
||||
#define writeb(v,c) ({ u8 __v = v; __iowmb(); __arch_putb(__v,c); __v; })
|
||||
#define writew(v,c) ({ u16 __v = v; __iowmb(); __arch_putw(__v,c); __v; })
|
||||
#define writel(v,c) ({ u32 __v = v; __iowmb(); __arch_putl(__v,c); __v; })
|
||||
|
7
arch/arm/include/asm/xen.h
Normal file
7
arch/arm/include/asm/xen.h
Normal file
@ -0,0 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* (C) 2020 EPAM Systems Inc.
|
||||
*/
|
||||
|
||||
extern unsigned long rom_pointer[];
|
||||
|
22
arch/arm/include/asm/xen/hypercall.h
Normal file
22
arch/arm/include/asm/xen/hypercall.h
Normal file
@ -0,0 +1,22 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* hypercall.h
|
||||
*
|
||||
* Linux-specific hypervisor handling.
|
||||
*
|
||||
* Stefano Stabellini <stefano.stabellini@eu.citrix.com>, Citrix, 2012
|
||||
*/
|
||||
|
||||
#ifndef _ASM_ARM_XEN_HYPERCALL_H
|
||||
#define _ASM_ARM_XEN_HYPERCALL_H
|
||||
|
||||
#include <xen/interface/xen.h>
|
||||
|
||||
int HYPERVISOR_xen_version(int cmd, void *arg);
|
||||
int HYPERVISOR_console_io(int cmd, int count, char *str);
|
||||
int HYPERVISOR_grant_table_op(unsigned int cmd, void *uop, unsigned int count);
|
||||
int HYPERVISOR_sched_op(int cmd, void *arg);
|
||||
int HYPERVISOR_event_channel_op(int cmd, void *arg);
|
||||
unsigned long HYPERVISOR_hvm_op(int op, void *arg);
|
||||
int HYPERVISOR_memory_op(unsigned int cmd, void *arg);
|
||||
#endif /* _ASM_ARM_XEN_HYPERCALL_H */
|
88
arch/arm/include/asm/xen/system.h
Normal file
88
arch/arm/include/asm/xen/system.h
Normal file
@ -0,0 +1,88 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* (C) 2014 Karim Allah Ahmed <karim.allah.ahmed@gmail.com>
|
||||
* (C) 2020, EPAM Systems Inc.
|
||||
*/
|
||||
#ifndef _ASM_ARM_XEN_SYSTEM_H
|
||||
#define _ASM_ARM_XEN_SYSTEM_H
|
||||
|
||||
#include <compiler.h>
|
||||
#include <asm/bitops.h>
|
||||
|
||||
/* If *ptr == old, then store new there (and return new).
|
||||
* Otherwise, return the old value.
|
||||
* Atomic.
|
||||
*/
|
||||
#define synch_cmpxchg(ptr, old, new) \
|
||||
({ __typeof__(*ptr) stored = old; \
|
||||
__atomic_compare_exchange_n(ptr, &stored, new, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? new : old; \
|
||||
})
|
||||
|
||||
/* As test_and_clear_bit, but using __ATOMIC_SEQ_CST */
|
||||
static inline int synch_test_and_clear_bit(int nr, volatile void *addr)
|
||||
{
|
||||
u8 *byte = ((u8 *)addr) + (nr >> 3);
|
||||
u8 bit = 1 << (nr & 7);
|
||||
u8 orig;
|
||||
|
||||
orig = __atomic_fetch_and(byte, ~bit, __ATOMIC_SEQ_CST);
|
||||
|
||||
return (orig & bit) != 0;
|
||||
}
|
||||
|
||||
/* As test_and_set_bit, but using __ATOMIC_SEQ_CST */
|
||||
static inline int synch_test_and_set_bit(int nr, volatile void *base)
|
||||
{
|
||||
u8 *byte = ((u8 *)base) + (nr >> 3);
|
||||
u8 bit = 1 << (nr & 7);
|
||||
u8 orig;
|
||||
|
||||
orig = __atomic_fetch_or(byte, bit, __ATOMIC_SEQ_CST);
|
||||
|
||||
return (orig & bit) != 0;
|
||||
}
|
||||
|
||||
/* As set_bit, but using __ATOMIC_SEQ_CST */
|
||||
static inline void synch_set_bit(int nr, volatile void *addr)
|
||||
{
|
||||
synch_test_and_set_bit(nr, addr);
|
||||
}
|
||||
|
||||
/* As clear_bit, but using __ATOMIC_SEQ_CST */
|
||||
static inline void synch_clear_bit(int nr, volatile void *addr)
|
||||
{
|
||||
synch_test_and_clear_bit(nr, addr);
|
||||
}
|
||||
|
||||
/* As test_bit, but with a following memory barrier. */
|
||||
//static inline int synch_test_bit(int nr, volatile void *addr)
|
||||
static inline int synch_test_bit(int nr, const void *addr)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = test_bit(nr, addr);
|
||||
barrier();
|
||||
return result;
|
||||
}
|
||||
|
||||
#define xchg(ptr, v) __atomic_exchange_n(ptr, v, __ATOMIC_SEQ_CST)
|
||||
#define xchg(ptr, v) __atomic_exchange_n(ptr, v, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define xen_mb() mb()
|
||||
#define xen_rmb() rmb()
|
||||
#define xen_wmb() wmb()
|
||||
|
||||
#define to_phys(x) ((unsigned long)(x))
|
||||
#define to_virt(x) ((void *)(x))
|
||||
|
||||
#define PFN_UP(x) (unsigned long)(((x) + PAGE_SIZE - 1) >> PAGE_SHIFT)
|
||||
#define PFN_DOWN(x) (unsigned long)((x) >> PAGE_SHIFT)
|
||||
#define PFN_PHYS(x) ((unsigned long)(x) << PAGE_SHIFT)
|
||||
#define PHYS_PFN(x) (unsigned long)((x) >> PAGE_SHIFT)
|
||||
|
||||
#define virt_to_pfn(_virt) (PFN_DOWN(to_phys(_virt)))
|
||||
#define virt_to_mfn(_virt) (PFN_DOWN(to_phys(_virt)))
|
||||
#define mfn_to_virt(_mfn) (to_virt(PFN_PHYS(_mfn)))
|
||||
#define pfn_to_virt(_pfn) (to_virt(PFN_PHYS(_pfn)))
|
||||
|
||||
#endif
|
@ -3,4 +3,4 @@
|
||||
# Copyright (c) 2016 Google, Inc
|
||||
|
||||
obj-$(CONFIG_ARCH_ASPEED) += ast_wdt.o
|
||||
obj-$(CONFIG_ASPEED_AST2500) += ast2500/ ast2500-board.o
|
||||
obj-$(CONFIG_ASPEED_AST2500) += ast2500/
|
||||
|
@ -1 +1,3 @@
|
||||
obj-y += lowlevel_init.o
|
||||
obj-y += board_common.o
|
||||
obj-y += clk_ast2500.o sdram_ast2500.o
|
||||
|
@ -28,31 +28,6 @@
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
void lowlevel_init(void)
|
||||
{
|
||||
/*
|
||||
* These two watchdogs need to be stopped as soon as possible,
|
||||
* otherwise the board might hang. By default they are set to
|
||||
* a very short timeout and even simple debug write to serial
|
||||
* console early in the init process might cause them to fire.
|
||||
*/
|
||||
struct ast_wdt *flash_addr_wdt =
|
||||
(struct ast_wdt *)(WDT_BASE +
|
||||
sizeof(struct ast_wdt) *
|
||||
AST_FLASH_ADDR_DETECT_WDT);
|
||||
|
||||
clrbits_le32(&flash_addr_wdt->ctrl, WDT_CTRL_EN);
|
||||
|
||||
#ifndef CONFIG_FIRMWARE_2ND_BOOT
|
||||
struct ast_wdt *sec_boot_wdt =
|
||||
(struct ast_wdt *)(WDT_BASE +
|
||||
sizeof(struct ast_wdt) *
|
||||
AST_2ND_BOOT_WDT);
|
||||
|
||||
clrbits_le32(&sec_boot_wdt->ctrl, WDT_CTRL_EN);
|
||||
#endif
|
||||
}
|
||||
|
||||
int board_init(void)
|
||||
{
|
||||
gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100;
|
41
arch/arm/mach-aspeed/ast2500/lowlevel_init.S
Normal file
41
arch/arm/mach-aspeed/ast2500/lowlevel_init.S
Normal file
@ -0,0 +1,41 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) ASPEED Technology Inc.
|
||||
*/
|
||||
#include <asm/arch/scu_ast2500.h>
|
||||
|
||||
/* registers for low level init */
|
||||
#define SCU_PROT_KEY 0x1e6e2000
|
||||
#define SCU_VGA_HANDSHAKE 0x1e6e2040
|
||||
#define SCU_HW_STRAP 0x1e6e2070
|
||||
#define SCU_HW_STRAP_CLR 0x1e6e207c
|
||||
#define WDT3_CTRL 0x1e78504c
|
||||
|
||||
.global lowlevel_init
|
||||
lowlevel_init:
|
||||
|
||||
/* unlock SCU */
|
||||
ldr r0, =SCU_PROT_KEY
|
||||
ldr r1, =SCU_UNLOCK_VALUE
|
||||
str r1, [r0]
|
||||
|
||||
/* set BMC FW as DRAM initializer */
|
||||
ldr r0, =SCU_VGA_HANDSHAKE
|
||||
ldr r1, [r0]
|
||||
orr r1, #0x80
|
||||
str r1, [r0]
|
||||
|
||||
/* set PERST# as LPC reset source if eSPI mode is enabled*/
|
||||
ldr r0, =SCU_HW_STRAP
|
||||
ldr r1, [r0]
|
||||
tst r1, #(0x1 << 25)
|
||||
ldrne r0, =SCU_HW_STRAP_CLR
|
||||
movne r1, #(0x1 << 14)
|
||||
strne r1, [r0]
|
||||
|
||||
/* disable WDT3 for SPI 3/4 bytes auto-detection */
|
||||
ldr r0, =WDT3_CTRL
|
||||
mov r1, #0x0
|
||||
str r1, [r0]
|
||||
|
||||
mov pc, lr
|
12
board/xen/xenguest_arm64/Kconfig
Normal file
12
board/xen/xenguest_arm64/Kconfig
Normal file
@ -0,0 +1,12 @@
|
||||
if TARGET_XENGUEST_ARM64
|
||||
|
||||
config SYS_BOARD
|
||||
default "xenguest_arm64"
|
||||
|
||||
config SYS_VENDOR
|
||||
default "xen"
|
||||
|
||||
config SYS_CONFIG_NAME
|
||||
default "xenguest_arm64"
|
||||
|
||||
endif
|
7
board/xen/xenguest_arm64/MAINTAINERS
Normal file
7
board/xen/xenguest_arm64/MAINTAINERS
Normal file
@ -0,0 +1,7 @@
|
||||
XEN GUEST FOR ARM64
|
||||
M: Andrii Anisov <andrii_anisov@epam.com>
|
||||
S: Maintained
|
||||
F: board/xen/xenguest_arm64/
|
||||
F: doc/board/xen/
|
||||
F: include/configs/xenguest_arm64.h
|
||||
F: configs/xenguest_arm64_defconfig
|
5
board/xen/xenguest_arm64/Makefile
Normal file
5
board/xen/xenguest_arm64/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2020 EPAM Systems Inc.
|
||||
|
||||
obj-y := xenguest_arm64.o
|
202
board/xen/xenguest_arm64/xenguest_arm64.c
Normal file
202
board/xen/xenguest_arm64/xenguest_arm64.c
Normal file
@ -0,0 +1,202 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* (C) 2013
|
||||
* David Feng <fenghua@phytium.com.cn>
|
||||
* Sharma Bhupesh <bhupesh.sharma@freescale.com>
|
||||
*
|
||||
* (C) 2020 EPAM Systems Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <xen.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/armv8/mmu.h>
|
||||
#include <asm/xen.h>
|
||||
#include <asm/xen/hypercall.h>
|
||||
#include <asm/xen/system.h>
|
||||
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#include <xen/gnttab.h>
|
||||
#include <xen/hvm.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int board_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use fdt provided by Xen: according to
|
||||
* https://www.kernel.org/doc/Documentation/arm64/booting.txt
|
||||
* x0 is the physical address of the device tree blob (dtb) in system RAM.
|
||||
* This is stored in rom_pointer during low level init.
|
||||
*/
|
||||
void *board_fdt_blob_setup(void)
|
||||
{
|
||||
if (fdt_magic(rom_pointer[0]) != FDT_MAGIC)
|
||||
return NULL;
|
||||
return (void *)rom_pointer[0];
|
||||
}
|
||||
|
||||
#define MAX_MEM_MAP_REGIONS 5
|
||||
static struct mm_region xen_mem_map[MAX_MEM_MAP_REGIONS];
|
||||
struct mm_region *mem_map = xen_mem_map;
|
||||
|
||||
static int get_next_memory_node(const void *blob, int mem)
|
||||
{
|
||||
do {
|
||||
mem = fdt_node_offset_by_prop_value(blob, mem,
|
||||
"device_type", "memory", 7);
|
||||
} while (!fdtdec_get_is_enabled(blob, mem));
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
static int setup_mem_map(void)
|
||||
{
|
||||
int i = 0, ret, mem, reg = 0;
|
||||
struct fdt_resource res;
|
||||
const void *blob = gd->fdt_blob;
|
||||
u64 gfn;
|
||||
phys_addr_t gnttab_base;
|
||||
phys_size_t gnttab_sz;
|
||||
|
||||
/*
|
||||
* Add "magic" region which is used by Xen to provide some essentials
|
||||
* for the guest: we need console and xenstore.
|
||||
*/
|
||||
ret = hvm_get_parameter_maintain_dcache(HVM_PARAM_CONSOLE_PFN, &gfn);
|
||||
if (ret < 0) {
|
||||
printf("%s: Can't get HVM_PARAM_CONSOLE_PFN, ret %d\n",
|
||||
__func__, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
xen_mem_map[i].virt = PFN_PHYS(gfn);
|
||||
xen_mem_map[i].phys = PFN_PHYS(gfn);
|
||||
xen_mem_map[i].size = PAGE_SIZE;
|
||||
xen_mem_map[i].attrs = (PTE_BLOCK_MEMTYPE(MT_NORMAL) |
|
||||
PTE_BLOCK_INNER_SHARE);
|
||||
i++;
|
||||
|
||||
ret = hvm_get_parameter_maintain_dcache(HVM_PARAM_STORE_PFN, &gfn);
|
||||
if (ret < 0) {
|
||||
printf("%s: Can't get HVM_PARAM_STORE_PFN, ret %d\n",
|
||||
__func__, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
xen_mem_map[i].virt = PFN_PHYS(gfn);
|
||||
xen_mem_map[i].phys = PFN_PHYS(gfn);
|
||||
xen_mem_map[i].size = PAGE_SIZE;
|
||||
xen_mem_map[i].attrs = (PTE_BLOCK_MEMTYPE(MT_NORMAL) |
|
||||
PTE_BLOCK_INNER_SHARE);
|
||||
i++;
|
||||
|
||||
/* Get Xen's suggested physical page assignments for the grant table. */
|
||||
get_gnttab_base(&gnttab_base, &gnttab_sz);
|
||||
|
||||
xen_mem_map[i].virt = gnttab_base;
|
||||
xen_mem_map[i].phys = gnttab_base;
|
||||
xen_mem_map[i].size = gnttab_sz;
|
||||
xen_mem_map[i].attrs = (PTE_BLOCK_MEMTYPE(MT_NORMAL) |
|
||||
PTE_BLOCK_INNER_SHARE);
|
||||
i++;
|
||||
|
||||
mem = get_next_memory_node(blob, -1);
|
||||
if (mem < 0) {
|
||||
printf("%s: Missing /memory node\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (; i < MAX_MEM_MAP_REGIONS; i++) {
|
||||
ret = fdt_get_resource(blob, mem, "reg", reg++, &res);
|
||||
if (ret == -FDT_ERR_NOTFOUND) {
|
||||
reg = 0;
|
||||
mem = get_next_memory_node(blob, mem);
|
||||
if (mem == -FDT_ERR_NOTFOUND)
|
||||
break;
|
||||
|
||||
ret = fdt_get_resource(blob, mem, "reg", reg++, &res);
|
||||
if (ret == -FDT_ERR_NOTFOUND)
|
||||
break;
|
||||
}
|
||||
if (ret != 0) {
|
||||
printf("No reg property for memory node\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
xen_mem_map[i].virt = (phys_addr_t)res.start;
|
||||
xen_mem_map[i].phys = (phys_addr_t)res.start;
|
||||
xen_mem_map[i].size = (phys_size_t)(res.end - res.start + 1);
|
||||
xen_mem_map[i].attrs = (PTE_BLOCK_MEMTYPE(MT_NORMAL) |
|
||||
PTE_BLOCK_INNER_SHARE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void enable_caches(void)
|
||||
{
|
||||
/* Re-setup the memory map as BSS gets cleared after relocation. */
|
||||
setup_mem_map();
|
||||
icache_enable();
|
||||
dcache_enable();
|
||||
}
|
||||
|
||||
/* Read memory settings from the Xen provided device tree. */
|
||||
int dram_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = fdtdec_setup_mem_size_base();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* Setup memory map, so MMU page table size can be estimated. */
|
||||
return setup_mem_map();
|
||||
}
|
||||
|
||||
int dram_init_banksize(void)
|
||||
{
|
||||
return fdtdec_setup_memory_banksize();
|
||||
}
|
||||
|
||||
/*
|
||||
* Board specific reset that is system reset.
|
||||
*/
|
||||
void reset_cpu(ulong addr)
|
||||
{
|
||||
}
|
||||
|
||||
int ft_system_setup(void *blob, struct bd_info *bd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ft_board_setup(void *blob, struct bd_info *bd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int board_early_init_f(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int print_cpuinfo(void)
|
||||
{
|
||||
printf("Xen virtual CPU\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void board_cleanup_before_linux(void)
|
||||
{
|
||||
xen_fini();
|
||||
}
|
||||
|
@ -1370,6 +1370,13 @@ config CMD_USB_MASS_STORAGE
|
||||
help
|
||||
USB mass storage support
|
||||
|
||||
config CMD_PVBLOCK
|
||||
bool "Xen para-virtualized block device"
|
||||
depends on XEN
|
||||
select PVBLOCK
|
||||
help
|
||||
Xen para-virtualized block device support
|
||||
|
||||
config CMD_VIRTIO
|
||||
bool "virtio"
|
||||
depends on VIRTIO
|
||||
|
@ -174,6 +174,7 @@ obj-$(CONFIG_CMD_DFU) += dfu.o
|
||||
obj-$(CONFIG_CMD_GPT) += gpt.o
|
||||
obj-$(CONFIG_CMD_ETHSW) += ethsw.o
|
||||
obj-$(CONFIG_CMD_AXI) += axi.o
|
||||
obj-$(CONFIG_CMD_PVBLOCK) += pvblock.o
|
||||
|
||||
# Power
|
||||
obj-$(CONFIG_CMD_PMIC) += pmic.o
|
||||
|
@ -130,5 +130,4 @@ U_BOOT_CMD(
|
||||
"demo hello <num> [<char>] Say hello\n"
|
||||
"demo light [<num>] Set or get the lights\n"
|
||||
"demo status <num> Get demo device status\n"
|
||||
"demo list List available demo devices"
|
||||
);
|
||||
|
30
cmd/pvblock.c
Normal file
30
cmd/pvblock.c
Normal file
@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2020 EPAM Systems Inc.
|
||||
*
|
||||
* XEN para-virtualized block device support
|
||||
*/
|
||||
|
||||
#include <blk.h>
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
|
||||
/* Current I/O Device */
|
||||
static int pvblock_curr_device;
|
||||
|
||||
int do_pvblock(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
||||
{
|
||||
return blk_common_cmd(argc, argv, IF_TYPE_PVBLOCK,
|
||||
&pvblock_curr_device);
|
||||
}
|
||||
|
||||
U_BOOT_CMD(pvblock, 5, 1, do_pvblock,
|
||||
"Xen para-virtualized block device",
|
||||
"info - show available block devices\n"
|
||||
"pvblock device [dev] - show or set current device\n"
|
||||
"pvblock part [dev] - print partition table of one or all devices\n"
|
||||
"pvblock read addr blk# cnt\n"
|
||||
"pvblock write addr blk# cnt - read/write `cnt'"
|
||||
" blocks starting at block `blk#'\n"
|
||||
" to/from memory address `addr'");
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include <nand.h>
|
||||
#include <of_live.h>
|
||||
#include <onenand_uboot.h>
|
||||
#include <pvblock.h>
|
||||
#include <scsi.h>
|
||||
#include <serial.h>
|
||||
#include <status_led.h>
|
||||
@ -56,6 +57,9 @@
|
||||
#include <timer.h>
|
||||
#include <trace.h>
|
||||
#include <watchdog.h>
|
||||
#ifdef CONFIG_XEN
|
||||
#include <xen.h>
|
||||
#endif
|
||||
#ifdef CONFIG_ADDR_MAP
|
||||
#include <asm/mmu.h>
|
||||
#endif
|
||||
@ -465,6 +469,23 @@ static int initr_mmc(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_XEN
|
||||
static int initr_xen(void)
|
||||
{
|
||||
xen_init();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PVBLOCK
|
||||
static int initr_pvblock(void)
|
||||
{
|
||||
puts("PVBLOCK: ");
|
||||
pvblock_init();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Tell if it's OK to load the environment early in boot.
|
||||
*
|
||||
@ -761,6 +782,12 @@ static init_fnc_t init_sequence_r[] = {
|
||||
#endif
|
||||
#ifdef CONFIG_MMC
|
||||
initr_mmc,
|
||||
#endif
|
||||
#ifdef CONFIG_XEN
|
||||
initr_xen,
|
||||
#endif
|
||||
#ifdef CONFIG_PVBLOCK
|
||||
initr_pvblock,
|
||||
#endif
|
||||
initr_env,
|
||||
#ifdef CONFIG_SYS_BOOTPARAMS_LEN
|
||||
|
@ -11,6 +11,8 @@ CONFIG_PRE_CON_BUF_ADDR=0x1e720000
|
||||
CONFIG_DEFAULT_DEVICE_TREE="ast2500-evb"
|
||||
CONFIG_USE_BOOTARGS=y
|
||||
CONFIG_BOOTARGS="console=ttyS4,115200n8 root=/dev/ram rw"
|
||||
CONFIG_USE_BOOTCOMMAND=y
|
||||
CONFIG_BOOTCOMMAND="bootm 20080000 20300000"
|
||||
CONFIG_PRE_CONSOLE_BUFFER=y
|
||||
# CONFIG_DISPLAY_CPUINFO is not set
|
||||
CONFIG_HUSH_PARSER=y
|
||||
@ -20,7 +22,6 @@ CONFIG_CMD_MMC=y
|
||||
CONFIG_CMD_DHCP=y
|
||||
CONFIG_CMD_MII=y
|
||||
CONFIG_CMD_PING=y
|
||||
CONFIG_OF_EMBED=y
|
||||
CONFIG_ENV_OVERWRITE=y
|
||||
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
|
||||
CONFIG_NET_RANDOM_ETHADDR=y
|
||||
|
60
configs/xenguest_arm64_defconfig
Normal file
60
configs/xenguest_arm64_defconfig
Normal file
@ -0,0 +1,60 @@
|
||||
CONFIG_ARM=y
|
||||
CONFIG_POSITION_INDEPENDENT=y
|
||||
CONFIG_SYS_TEXT_BASE=0x40080000
|
||||
CONFIG_SYS_MALLOC_F_LEN=0x2000
|
||||
CONFIG_IDENT_STRING=" xenguest"
|
||||
CONFIG_TARGET_XENGUEST_ARM64=y
|
||||
CONFIG_BOOTDELAY=10
|
||||
|
||||
CONFIG_SYS_PROMPT="xenguest# "
|
||||
|
||||
CONFIG_CMD_NET=n
|
||||
CONFIG_CMD_BDI=n
|
||||
CONFIG_CMD_BOOTD=n
|
||||
CONFIG_CMD_BOOTEFI=n
|
||||
CONFIG_CMD_BOOTEFI_HELLO_COMPILE=n
|
||||
CONFIG_CMD_ELF=n
|
||||
CONFIG_CMD_EXT4=y
|
||||
CONFIG_CMD_FAT=y
|
||||
CONFIG_CMD_GO=n
|
||||
CONFIG_CMD_RUN=n
|
||||
CONFIG_CMD_IMI=n
|
||||
CONFIG_CMD_IMLS=n
|
||||
CONFIG_CMD_XIMG=n
|
||||
CONFIG_CMD_EXPORTENV=n
|
||||
CONFIG_CMD_IMPORTENV=n
|
||||
CONFIG_CMD_EDITENV=n
|
||||
CONFIG_CMD_ENV_EXISTS=n
|
||||
CONFIG_CMD_MEMORY=y
|
||||
CONFIG_CMD_CRC32=n
|
||||
CONFIG_CMD_DM=n
|
||||
CONFIG_CMD_LOADB=n
|
||||
CONFIG_CMD_LOADS=n
|
||||
CONFIG_CMD_FLASH=n
|
||||
CONFIG_CMD_GPT=n
|
||||
CONFIG_CMD_FPGA=n
|
||||
CONFIG_CMD_ECHO=n
|
||||
CONFIG_CMD_ITEST=n
|
||||
CONFIG_CMD_SOURCE=n
|
||||
CONFIG_CMD_SETEXPR=n
|
||||
CONFIG_CMD_MISC=n
|
||||
CONFIG_CMD_UNZIP=n
|
||||
CONFIG_CMD_LZMADEC=n
|
||||
CONFIG_CMD_SAVEENV=n
|
||||
CONFIG_CMD_UMS=n
|
||||
|
||||
CONFIG_CMD_PVBLOCK=y
|
||||
|
||||
#CONFIG_USB=n
|
||||
# CONFIG_ISO_PARTITION is not set
|
||||
|
||||
#CONFIG_EFI_PARTITION=y
|
||||
# CONFIG_EFI_LOADER is not set
|
||||
|
||||
CONFIG_DM=y
|
||||
# CONFIG_MMC is not set
|
||||
CONFIG_DM_SERIAL=y
|
||||
# CONFIG_REQUIRE_SERIAL_CONSOLE is not set
|
||||
|
||||
CONFIG_OF_BOARD=y
|
||||
CONFIG_OF_LIBFDT=y
|
@ -149,6 +149,7 @@ void dev_print (struct blk_desc *dev_desc)
|
||||
case IF_TYPE_MMC:
|
||||
case IF_TYPE_USB:
|
||||
case IF_TYPE_NVME:
|
||||
case IF_TYPE_PVBLOCK:
|
||||
printf ("Vendor: %s Rev: %s Prod: %s\n",
|
||||
dev_desc->vendor,
|
||||
dev_desc->revision,
|
||||
@ -288,6 +289,9 @@ static void print_part_header(const char *type, struct blk_desc *dev_desc)
|
||||
case IF_TYPE_NVME:
|
||||
puts ("NVMe");
|
||||
break;
|
||||
case IF_TYPE_PVBLOCK:
|
||||
puts("PV BLOCK");
|
||||
break;
|
||||
case IF_TYPE_VIRTIO:
|
||||
puts("VirtIO");
|
||||
break;
|
||||
|
@ -22,4 +22,5 @@ Board-specific doc
|
||||
st/index
|
||||
tbs/index
|
||||
toradex/index
|
||||
xen/index
|
||||
xilinx/index
|
||||
|
9
doc/board/xen/index.rst
Normal file
9
doc/board/xen/index.rst
Normal file
@ -0,0 +1,9 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
XenGuestARM64
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
xenguest_arm64
|
81
doc/board/xen/xenguest_arm64.rst
Normal file
81
doc/board/xen/xenguest_arm64.rst
Normal file
@ -0,0 +1,81 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
Xen guest ARM64 board
|
||||
=====================
|
||||
|
||||
This board specification
|
||||
------------------------
|
||||
|
||||
This board is to be run as a virtual Xen [1] guest with U-boot as its primary
|
||||
bootloader. Xen is a type 1 hypervisor that allows multiple operating systems
|
||||
to run simultaneously on a single physical server. Xen is capable of running
|
||||
virtual machines in both full virtualization and para-virtualization (PV)
|
||||
modes. Xen runs virtual machines, which are called “domains”.
|
||||
|
||||
Paravirtualized drivers are a special type of device drivers that are used in
|
||||
a guest system in the Xen domain and perform I/O operations using a special
|
||||
interface provided by the virtualization system and the host system.
|
||||
|
||||
Xen support for U-boot is implemented by introducing a new Xen guest ARM64
|
||||
board and porting essential drivers from MiniOS [3] as well as some of the work
|
||||
previously done by NXP [4]:
|
||||
|
||||
- PV block device frontend driver with XenStore based device enumeration and
|
||||
UCLASS_PVBLOCK class;
|
||||
- PV serial console device frontend driver;
|
||||
- Xen hypervisor support with minimal set of the essential headers adapted from
|
||||
the Linux kernel;
|
||||
- Xen grant table support;
|
||||
- Xen event channel support in polling mode;
|
||||
- XenBus support;
|
||||
- dynamic RAM size as defined in the device tree instead of the statically
|
||||
defined values;
|
||||
- position-independent pre-relocation code is used as we cannot statically
|
||||
define any start addresses at compile time which is up to Xen to choose at
|
||||
run-time;
|
||||
- new defconfig introduced: xenguest_arm64_defconfig.
|
||||
|
||||
|
||||
Board limitations
|
||||
-----------------
|
||||
|
||||
1. U-boot runs without MMU enabled at the early stages.
|
||||
According to Xen on ARM ABI (xen/include/public/arch-arm.h): all memory
|
||||
which is shared with other entities in the system (including the hypervisor
|
||||
and other guests) must reside in memory which is mapped as Normal Inner
|
||||
Write-Back Outer Write-Back Inner-Shareable.
|
||||
Thus, page attributes must be equally set for all the entities working with
|
||||
that page.
|
||||
Before MMU is set up the data cache is turned off and pages are seen by the
|
||||
vCPU and Xen in different ways - cacheable by Xen and non-cacheable by vCPU.
|
||||
So it means that manual data cache maintenance is required at the early
|
||||
stages.
|
||||
|
||||
2. No serial console until MMU is up.
|
||||
Because data cache maintenance is required until the MMU setup the
|
||||
early/debug serial console is not implemented. Therefore, we do not have
|
||||
usual prints like U-boot’s banner etc. until the serial driver is
|
||||
initialized.
|
||||
|
||||
3. Single RAM bank supported.
|
||||
If a Xen guest is given much memory it is possible that Xen allocates two
|
||||
memory banks for it. The first one is allocated under 4GB address space and
|
||||
in some cases may represent the whole guest’s memory. It is assumed that
|
||||
U-boot most likely won’t require high memory bank for its work andlaunching
|
||||
OS, so it is enough to take the first one.
|
||||
|
||||
|
||||
Board default configuration
|
||||
---------------------------
|
||||
|
||||
One can select the configuration as follows:
|
||||
|
||||
- make xenguest_arm64_defconfig
|
||||
|
||||
[1] - https://xenproject.org/
|
||||
|
||||
[2] - https://wiki.xenproject.org/wiki/Paravirtualization_(PV)
|
||||
|
||||
[3] - https://wiki.xenproject.org/wiki/Mini-OS
|
||||
|
||||
[4] - https://source.codeaurora.org/external/imx/uboot-imx/tree/?h=imx_v2018.03_4.14.98_2.0.0_ga
|
@ -136,6 +136,8 @@ source "drivers/w1-eeprom/Kconfig"
|
||||
|
||||
source "drivers/watchdog/Kconfig"
|
||||
|
||||
source "drivers/xen/Kconfig"
|
||||
|
||||
config PHYS_TO_BUS
|
||||
bool "Custom physical to bus address mapping"
|
||||
help
|
||||
|
@ -29,6 +29,7 @@ obj-$(CONFIG_$(SPL_)REMOTEPROC) += remoteproc/
|
||||
obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm/
|
||||
obj-$(CONFIG_$(SPL_TPL_)ACPI_PMC) += power/acpi_pmc/
|
||||
obj-$(CONFIG_$(SPL_)BOARD) += board/
|
||||
obj-$(CONFIG_XEN) += xen/
|
||||
|
||||
ifndef CONFIG_TPL_BUILD
|
||||
ifdef CONFIG_SPL_BUILD
|
||||
|
@ -28,6 +28,7 @@ static const char *if_typename_str[IF_TYPE_COUNT] = {
|
||||
[IF_TYPE_NVME] = "nvme",
|
||||
[IF_TYPE_EFI] = "efi",
|
||||
[IF_TYPE_VIRTIO] = "virtio",
|
||||
[IF_TYPE_PVBLOCK] = "pvblock",
|
||||
};
|
||||
|
||||
static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
|
||||
@ -43,6 +44,7 @@ static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
|
||||
[IF_TYPE_NVME] = UCLASS_NVME,
|
||||
[IF_TYPE_EFI] = UCLASS_EFI,
|
||||
[IF_TYPE_VIRTIO] = UCLASS_VIRTIO,
|
||||
[IF_TYPE_PVBLOCK] = UCLASS_PVBLOCK,
|
||||
};
|
||||
|
||||
static enum if_type if_typename_to_iftype(const char *if_typename)
|
||||
|
@ -191,6 +191,13 @@ config PCIE_INTEL_FPGA
|
||||
Say Y here if you want to enable PCIe controller support on Intel
|
||||
FPGA, example Stratix 10.
|
||||
|
||||
config PCIE_IPROC
|
||||
bool "Iproc PCIe support"
|
||||
depends on DM_PCI
|
||||
help
|
||||
Broadcom iProc PCIe controller driver.
|
||||
Say Y here if you want to enable Broadcom iProc PCIe controller,
|
||||
|
||||
config PCI_MVEBU
|
||||
bool "Enable Armada XP/38x PCIe driver"
|
||||
depends on ARCH_MVEBU
|
||||
|
@ -30,6 +30,7 @@ obj-$(CONFIG_SH4_PCI) += pci_sh4.o
|
||||
obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o
|
||||
obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o
|
||||
obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o
|
||||
obj-$(CONFIG_PCIE_IPROC) += pcie_iproc.o
|
||||
obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o
|
||||
obj-$(CONFIG_PCIE_DW_MVEBU) += pcie_dw_mvebu.o
|
||||
obj-$(CONFIG_PCIE_FSL) += pcie_fsl.o pcie_fsl_fixup.o
|
||||
|
@ -1179,6 +1179,48 @@ ulong pci_conv_size_to_32(ulong old, ulong value, uint offset,
|
||||
return value;
|
||||
}
|
||||
|
||||
int pci_get_dma_regions(struct udevice *dev, struct pci_region *memp, int index)
|
||||
{
|
||||
int pci_addr_cells, addr_cells, size_cells;
|
||||
int cells_per_record;
|
||||
const u32 *prop;
|
||||
int len;
|
||||
int i = 0;
|
||||
|
||||
prop = ofnode_get_property(dev_ofnode(dev), "dma-ranges", &len);
|
||||
if (!prop) {
|
||||
log_err("PCI: Device '%s': Cannot decode dma-ranges\n",
|
||||
dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pci_addr_cells = ofnode_read_simple_addr_cells(dev_ofnode(dev));
|
||||
addr_cells = ofnode_read_simple_addr_cells(dev_ofnode(dev->parent));
|
||||
size_cells = ofnode_read_simple_size_cells(dev_ofnode(dev));
|
||||
|
||||
/* PCI addresses are always 3-cells */
|
||||
len /= sizeof(u32);
|
||||
cells_per_record = pci_addr_cells + addr_cells + size_cells;
|
||||
debug("%s: len=%d, cells_per_record=%d\n", __func__, len,
|
||||
cells_per_record);
|
||||
|
||||
while (len) {
|
||||
memp->bus_start = fdtdec_get_number(prop + 1, 2);
|
||||
prop += pci_addr_cells;
|
||||
memp->phys_start = fdtdec_get_number(prop, addr_cells);
|
||||
prop += addr_cells;
|
||||
memp->size = fdtdec_get_number(prop, size_cells);
|
||||
prop += size_cells;
|
||||
|
||||
if (i == index)
|
||||
return 0;
|
||||
i++;
|
||||
len -= cells_per_record;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int pci_get_regions(struct udevice *dev, struct pci_region **iop,
|
||||
struct pci_region **memp, struct pci_region **prefp)
|
||||
{
|
||||
|
1287
drivers/pci/pcie_iproc.c
Normal file
1287
drivers/pci/pcie_iproc.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -822,6 +822,13 @@ config MPC8XX_CONS
|
||||
depends on MPC8xx
|
||||
default y
|
||||
|
||||
config XEN_SERIAL
|
||||
bool "XEN serial support"
|
||||
depends on XEN
|
||||
help
|
||||
If built without DM support, then requires Xen
|
||||
to be built with CONFIG_VERBOSE_DEBUG.
|
||||
|
||||
choice
|
||||
prompt "Console port"
|
||||
default 8xx_CONS_SMC1
|
||||
|
@ -70,6 +70,7 @@ obj-$(CONFIG_OWL_SERIAL) += serial_owl.o
|
||||
obj-$(CONFIG_OMAP_SERIAL) += serial_omap.o
|
||||
obj-$(CONFIG_MTK_SERIAL) += serial_mtk.o
|
||||
obj-$(CONFIG_SIFIVE_SERIAL) += serial_sifive.o
|
||||
obj-$(CONFIG_XEN_SERIAL) += serial_xen.o
|
||||
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
obj-$(CONFIG_USB_TTY) += usbtty.o
|
||||
|
182
drivers/serial/serial_xen.c
Normal file
182
drivers/serial/serial_xen.c
Normal file
@ -0,0 +1,182 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* (C) 2018 NXP
|
||||
* (C) 2020 EPAM Systems Inc.
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <dm.h>
|
||||
#include <serial.h>
|
||||
#include <watchdog.h>
|
||||
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include <xen/hvm.h>
|
||||
#include <xen/events.h>
|
||||
|
||||
#include <xen/interface/sched.h>
|
||||
#include <xen/interface/hvm/hvm_op.h>
|
||||
#include <xen/interface/hvm/params.h>
|
||||
#include <xen/interface/io/console.h>
|
||||
#include <xen/interface/io/ring.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
u32 console_evtchn;
|
||||
|
||||
/*
|
||||
* struct xen_uart_priv - Structure representing a Xen UART info
|
||||
* @intf: Console I/O interface for Xen guest OSes
|
||||
* @evtchn: Console event channel
|
||||
*/
|
||||
struct xen_uart_priv {
|
||||
struct xencons_interface *intf;
|
||||
u32 evtchn;
|
||||
};
|
||||
|
||||
int xen_serial_setbrg(struct udevice *dev, int baudrate)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xen_serial_probe(struct udevice *dev)
|
||||
{
|
||||
struct xen_uart_priv *priv = dev_get_priv(dev);
|
||||
u64 val = 0;
|
||||
unsigned long gfn;
|
||||
int ret;
|
||||
|
||||
ret = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &val);
|
||||
if (ret < 0 || val == 0)
|
||||
return ret;
|
||||
|
||||
priv->evtchn = val;
|
||||
console_evtchn = val;
|
||||
|
||||
ret = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
|
||||
gfn = val;
|
||||
priv->intf = (struct xencons_interface *)(gfn << XEN_PAGE_SHIFT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xen_serial_pending(struct udevice *dev, bool input)
|
||||
{
|
||||
struct xen_uart_priv *priv = dev_get_priv(dev);
|
||||
struct xencons_interface *intf = priv->intf;
|
||||
|
||||
if (!input || intf->in_cons == intf->in_prod)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int xen_serial_getc(struct udevice *dev)
|
||||
{
|
||||
struct xen_uart_priv *priv = dev_get_priv(dev);
|
||||
struct xencons_interface *intf = priv->intf;
|
||||
XENCONS_RING_IDX cons;
|
||||
char c;
|
||||
|
||||
while (intf->in_cons == intf->in_prod)
|
||||
mb(); /* wait */
|
||||
|
||||
cons = intf->in_cons;
|
||||
mb(); /* get pointers before reading ring */
|
||||
|
||||
c = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];
|
||||
|
||||
mb(); /* read ring before consuming */
|
||||
intf->in_cons = cons;
|
||||
|
||||
notify_remote_via_evtchn(priv->evtchn);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static int __write_console(struct udevice *dev, const char *data, int len)
|
||||
{
|
||||
struct xen_uart_priv *priv = dev_get_priv(dev);
|
||||
struct xencons_interface *intf = priv->intf;
|
||||
XENCONS_RING_IDX cons, prod;
|
||||
int sent = 0;
|
||||
|
||||
cons = intf->out_cons;
|
||||
prod = intf->out_prod;
|
||||
mb(); /* Update pointer */
|
||||
|
||||
WARN_ON((prod - cons) > sizeof(intf->out));
|
||||
|
||||
while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
|
||||
intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
|
||||
|
||||
mb(); /* Update data before pointer */
|
||||
intf->out_prod = prod;
|
||||
|
||||
if (sent)
|
||||
notify_remote_via_evtchn(priv->evtchn);
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
static int write_console(struct udevice *dev, const char *data, int len)
|
||||
{
|
||||
/*
|
||||
* Make sure the whole buffer is emitted, polling if
|
||||
* necessary. We don't ever want to rely on the hvc daemon
|
||||
* because the most interesting console output is when the
|
||||
* kernel is crippled.
|
||||
*/
|
||||
while (len) {
|
||||
int sent = __write_console(dev, data, len);
|
||||
|
||||
data += sent;
|
||||
len -= sent;
|
||||
|
||||
if (unlikely(len))
|
||||
HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xen_serial_putc(struct udevice *dev, const char ch)
|
||||
{
|
||||
write_console(dev, &ch, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_serial_ops xen_serial_ops = {
|
||||
.putc = xen_serial_putc,
|
||||
.getc = xen_serial_getc,
|
||||
.pending = xen_serial_pending,
|
||||
};
|
||||
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
static const struct udevice_id xen_serial_ids[] = {
|
||||
{ .compatible = "xen,xen" },
|
||||
{ }
|
||||
};
|
||||
#endif
|
||||
|
||||
U_BOOT_DRIVER(serial_xen) = {
|
||||
.name = "serial_xen",
|
||||
.id = UCLASS_SERIAL,
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
.of_match = xen_serial_ids,
|
||||
#endif
|
||||
.priv_auto_alloc_size = sizeof(struct xen_uart_priv),
|
||||
.probe = xen_serial_probe,
|
||||
.ops = &xen_serial_ops,
|
||||
#if !CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
#endif
|
||||
};
|
||||
|
@ -33,7 +33,6 @@
|
||||
|
||||
#define USB_NET_NAME "usb_ether"
|
||||
|
||||
#define atomic_read
|
||||
extern struct platform_data brd;
|
||||
|
||||
|
||||
|
@ -10,10 +10,6 @@
|
||||
|
||||
#define platform_data device_data
|
||||
|
||||
#ifndef wmb
|
||||
#define wmb() asm volatile ("" : : : "memory")
|
||||
#endif
|
||||
|
||||
#define msleep(a) udelay(a * 1000)
|
||||
|
||||
/*
|
||||
|
10
drivers/xen/Kconfig
Normal file
10
drivers/xen/Kconfig
Normal file
@ -0,0 +1,10 @@
|
||||
config PVBLOCK
|
||||
bool "Xen para-virtualized block device"
|
||||
depends on DM
|
||||
select BLK
|
||||
select HAVE_BLOCK_DEVICE
|
||||
help
|
||||
This driver implements the front-end of the Xen virtual
|
||||
block device driver. It communicates with a back-end driver
|
||||
in another domain which drives the actual block device.
|
||||
|
10
drivers/xen/Makefile
Normal file
10
drivers/xen/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2020 EPAM Systems Inc.
|
||||
|
||||
obj-y += hypervisor.o
|
||||
obj-y += events.o
|
||||
obj-y += xenbus.o
|
||||
obj-y += gnttab.o
|
||||
|
||||
obj-$(CONFIG_PVBLOCK) += pvblock.o
|
199
drivers/xen/events.c
Normal file
199
drivers/xen/events.c
Normal file
@ -0,0 +1,199 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* (C) 2003 - Rolf Neugebauer - Intel Research Cambridge
|
||||
* (C) 2005 - Grzegorz Milos - Intel Research Cambridge
|
||||
* (C) 2020 - EPAM Systems Inc.
|
||||
*
|
||||
* File: events.c [1]
|
||||
* Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
|
||||
* Changes: Grzegorz Milos (gm281@cam.ac.uk)
|
||||
*
|
||||
* Date: Jul 2003, changes Jun 2005
|
||||
*
|
||||
* Description: Deals with events received on event channels
|
||||
*
|
||||
* [1] - http://xenbits.xen.org/gitweb/?p=mini-os.git;a=summary
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <log.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/xen/system.h>
|
||||
|
||||
#include <xen/events.h>
|
||||
#include <xen/hvm.h>
|
||||
|
||||
extern u32 console_evtchn;
|
||||
|
||||
#define NR_EVS 1024
|
||||
|
||||
/**
|
||||
* struct _ev_action - represents a event handler.
|
||||
*
|
||||
* Chaining or sharing is not allowed
|
||||
*/
|
||||
struct _ev_action {
|
||||
void (*handler)(evtchn_port_t port, struct pt_regs *regs, void *data);
|
||||
void *data;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
static struct _ev_action ev_actions[NR_EVS];
|
||||
void default_handler(evtchn_port_t port, struct pt_regs *regs, void *data);
|
||||
|
||||
static unsigned long bound_ports[NR_EVS / (8 * sizeof(unsigned long))];
|
||||
|
||||
void unbind_all_ports(void)
|
||||
{
|
||||
int i;
|
||||
int cpu = 0;
|
||||
struct shared_info *s = HYPERVISOR_shared_info;
|
||||
struct vcpu_info *vcpu_info = &s->vcpu_info[cpu];
|
||||
|
||||
for (i = 0; i < NR_EVS; i++) {
|
||||
if (i == console_evtchn)
|
||||
continue;
|
||||
if (test_and_clear_bit(i, bound_ports)) {
|
||||
printf("port %d still bound!\n", i);
|
||||
unbind_evtchn(i);
|
||||
}
|
||||
}
|
||||
vcpu_info->evtchn_upcall_pending = 0;
|
||||
vcpu_info->evtchn_pending_sel = 0;
|
||||
}
|
||||
|
||||
int do_event(evtchn_port_t port, struct pt_regs *regs)
|
||||
{
|
||||
struct _ev_action *action;
|
||||
|
||||
clear_evtchn(port);
|
||||
|
||||
if (port >= NR_EVS) {
|
||||
printk("WARN: do_event(): Port number too large: %d\n", port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
action = &ev_actions[port];
|
||||
action->count++;
|
||||
|
||||
/* call the handler */
|
||||
action->handler(port, regs, action->data);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
evtchn_port_t bind_evtchn(evtchn_port_t port,
|
||||
void (*handler)(evtchn_port_t, struct pt_regs *, void *),
|
||||
void *data)
|
||||
{
|
||||
if (ev_actions[port].handler != default_handler)
|
||||
printf("WARN: Handler for port %d already registered, replacing\n",
|
||||
port);
|
||||
|
||||
ev_actions[port].data = data;
|
||||
wmb();
|
||||
ev_actions[port].handler = handler;
|
||||
synch_set_bit(port, bound_ports);
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* unbind_evtchn() - Unbind event channel for selected port
|
||||
*/
|
||||
void unbind_evtchn(evtchn_port_t port)
|
||||
{
|
||||
struct evtchn_close close;
|
||||
int rc;
|
||||
|
||||
if (ev_actions[port].handler == default_handler)
|
||||
debug("Default handler for port %d when unbinding\n", port);
|
||||
mask_evtchn(port);
|
||||
clear_evtchn(port);
|
||||
|
||||
ev_actions[port].handler = default_handler;
|
||||
wmb();
|
||||
ev_actions[port].data = NULL;
|
||||
synch_clear_bit(port, bound_ports);
|
||||
|
||||
close.port = port;
|
||||
rc = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
|
||||
if (rc)
|
||||
printf("WARN: close_port %d failed rc=%d. ignored\n", port, rc);
|
||||
}
|
||||
|
||||
void default_handler(evtchn_port_t port, struct pt_regs *regs, void *ignore)
|
||||
{
|
||||
debug("[Port %d] - event received\n", port);
|
||||
}
|
||||
|
||||
/**
|
||||
* evtchn_alloc_unbound() - Create a port available to the pal for
|
||||
* exchanging notifications.
|
||||
*
|
||||
* Unfortunate confusion of terminology: the port is unbound as far
|
||||
* as Xen is concerned, but we automatically bind a handler to it.
|
||||
*
|
||||
* Return: The result of the hypervisor call.
|
||||
*/
|
||||
int evtchn_alloc_unbound(domid_t pal,
|
||||
void (*handler)(evtchn_port_t, struct pt_regs *, void *),
|
||||
void *data, evtchn_port_t *port)
|
||||
{
|
||||
int rc;
|
||||
|
||||
struct evtchn_alloc_unbound op;
|
||||
|
||||
op.dom = DOMID_SELF;
|
||||
op.remote_dom = pal;
|
||||
rc = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &op);
|
||||
if (rc) {
|
||||
printf("ERROR: alloc_unbound failed with rc=%d", rc);
|
||||
return rc;
|
||||
}
|
||||
if (!handler)
|
||||
handler = default_handler;
|
||||
*port = bind_evtchn(op.port, handler, data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* eventchn_poll() - Event channel polling function
|
||||
*
|
||||
* Check and process any pending events
|
||||
*/
|
||||
void eventchn_poll(void)
|
||||
{
|
||||
do_hypervisor_callback(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* init_events() - Initialize event handler
|
||||
*
|
||||
* Initially all events are without a handler and disabled.
|
||||
*/
|
||||
void init_events(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
for (i = 0; i < NR_EVS; i++) {
|
||||
ev_actions[i].handler = default_handler;
|
||||
mask_evtchn(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fini_events() - Close all ports
|
||||
*
|
||||
* Mask and clear event channels. Close port using EVTCHNOP_close
|
||||
* hypercall.
|
||||
*/
|
||||
void fini_events(void)
|
||||
{
|
||||
debug("%s\n", __func__);
|
||||
/* Dealloc all events */
|
||||
unbind_all_ports();
|
||||
}
|
||||
|
216
drivers/xen/gnttab.c
Normal file
216
drivers/xen/gnttab.c
Normal file
@ -0,0 +1,216 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* (C) 2006 - Cambridge University
|
||||
* (C) 2020 - EPAM Systems Inc.
|
||||
*
|
||||
* File: gnttab.c [1]
|
||||
* Author: Steven Smith (sos22@cam.ac.uk)
|
||||
* Changes: Grzegorz Milos (gm281@cam.ac.uk)
|
||||
*
|
||||
* Date: July 2006
|
||||
*
|
||||
* Description: Simple grant tables implementation. About as stupid as it's
|
||||
* possible to be and still work.
|
||||
*
|
||||
* [1] - http://xenbits.xen.org/gitweb/?p=mini-os.git;a=summary
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <asm/armv8/mmu.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/xen/system.h>
|
||||
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include <xen/gnttab.h>
|
||||
#include <xen/hvm.h>
|
||||
|
||||
#include <xen/interface/memory.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define NR_RESERVED_ENTRIES 8
|
||||
|
||||
/* NR_GRANT_FRAMES must be less than or equal to that configured in Xen */
|
||||
#define NR_GRANT_FRAMES 1
|
||||
#define NR_GRANT_ENTRIES (NR_GRANT_FRAMES * PAGE_SIZE / sizeof(struct grant_entry_v1))
|
||||
|
||||
static struct grant_entry_v1 *gnttab_table;
|
||||
static grant_ref_t gnttab_list[NR_GRANT_ENTRIES];
|
||||
|
||||
static void put_free_entry(grant_ref_t ref)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
gnttab_list[ref] = gnttab_list[0];
|
||||
gnttab_list[0] = ref;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static grant_ref_t get_free_entry(void)
|
||||
{
|
||||
unsigned int ref;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
ref = gnttab_list[0];
|
||||
BUG_ON(ref < NR_RESERVED_ENTRIES || ref >= NR_GRANT_ENTRIES);
|
||||
gnttab_list[0] = gnttab_list[ref];
|
||||
local_irq_restore(flags);
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnttab_grant_access() - Allow access to the given frame.
|
||||
* The function creates an entry in the grant table according
|
||||
* to the specified parameters.
|
||||
* @domid: the id of the domain for which access is allowed
|
||||
* @frame: the number of the shared frame
|
||||
* @readonly: determines whether the frame is shared read-only or read-write
|
||||
*
|
||||
* Return: relevant grant reference
|
||||
*/
|
||||
grant_ref_t gnttab_grant_access(domid_t domid, unsigned long frame, int readonly)
|
||||
{
|
||||
grant_ref_t ref;
|
||||
|
||||
ref = get_free_entry();
|
||||
gnttab_table[ref].frame = frame;
|
||||
gnttab_table[ref].domid = domid;
|
||||
wmb();
|
||||
readonly *= GTF_readonly;
|
||||
gnttab_table[ref].flags = GTF_permit_access | readonly;
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnttab_end_access() - End of memory sharing. The function invalidates
|
||||
* the entry in the grant table.
|
||||
*/
|
||||
int gnttab_end_access(grant_ref_t ref)
|
||||
{
|
||||
u16 flags, nflags;
|
||||
|
||||
BUG_ON(ref >= NR_GRANT_ENTRIES || ref < NR_RESERVED_ENTRIES);
|
||||
|
||||
nflags = gnttab_table[ref].flags;
|
||||
do {
|
||||
if ((flags = nflags) & (GTF_reading | GTF_writing)) {
|
||||
printf("WARNING: g.e. still in use! (%x)\n", flags);
|
||||
return 0;
|
||||
}
|
||||
} while ((nflags = synch_cmpxchg(&gnttab_table[ref].flags, flags, 0)) !=
|
||||
flags);
|
||||
|
||||
put_free_entry(ref);
|
||||
return 1;
|
||||
}
|
||||
|
||||
grant_ref_t gnttab_alloc_and_grant(void **map)
|
||||
{
|
||||
unsigned long mfn;
|
||||
grant_ref_t gref;
|
||||
|
||||
*map = (void *)memalign(PAGE_SIZE, PAGE_SIZE);
|
||||
mfn = virt_to_mfn(*map);
|
||||
gref = gnttab_grant_access(0, mfn, 0);
|
||||
return gref;
|
||||
}
|
||||
|
||||
static const char * const gnttabop_error_msgs[] = GNTTABOP_error_msgs;
|
||||
|
||||
const char *gnttabop_error(int16_t status)
|
||||
{
|
||||
status = -status;
|
||||
if (status < 0 || status >= ARRAY_SIZE(gnttabop_error_msgs))
|
||||
return "bad status";
|
||||
else
|
||||
return gnttabop_error_msgs[status];
|
||||
}
|
||||
|
||||
/* Get Xen's suggested physical page assignments for the grant table. */
|
||||
void get_gnttab_base(phys_addr_t *gnttab_base, phys_size_t *gnttab_sz)
|
||||
{
|
||||
const void *blob = gd->fdt_blob;
|
||||
struct fdt_resource res;
|
||||
int mem;
|
||||
|
||||
mem = fdt_node_offset_by_compatible(blob, -1, "xen,xen");
|
||||
if (mem < 0) {
|
||||
printf("No xen,xen compatible found\n");
|
||||
BUG();
|
||||
}
|
||||
|
||||
mem = fdt_get_resource(blob, mem, "reg", 0, &res);
|
||||
if (mem == -FDT_ERR_NOTFOUND) {
|
||||
printf("No grant table base in the device tree\n");
|
||||
BUG();
|
||||
}
|
||||
|
||||
*gnttab_base = (phys_addr_t)res.start;
|
||||
if (gnttab_sz)
|
||||
*gnttab_sz = (phys_size_t)(res.end - res.start + 1);
|
||||
|
||||
debug("FDT suggests grant table base at %llx\n",
|
||||
*gnttab_base);
|
||||
}
|
||||
|
||||
void init_gnttab(void)
|
||||
{
|
||||
struct xen_add_to_physmap xatp;
|
||||
struct gnttab_setup_table setup;
|
||||
xen_pfn_t frames[NR_GRANT_FRAMES];
|
||||
int i, rc;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
for (i = NR_RESERVED_ENTRIES; i < NR_GRANT_ENTRIES; i++)
|
||||
put_free_entry(i);
|
||||
|
||||
get_gnttab_base((phys_addr_t *)&gnttab_table, NULL);
|
||||
|
||||
for (i = 0; i < NR_GRANT_FRAMES; i++) {
|
||||
xatp.domid = DOMID_SELF;
|
||||
xatp.size = 0;
|
||||
xatp.space = XENMAPSPACE_grant_table;
|
||||
xatp.idx = i;
|
||||
xatp.gpfn = PFN_DOWN((unsigned long)gnttab_table) + i;
|
||||
rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp);
|
||||
if (rc)
|
||||
printf("XENMEM_add_to_physmap failed; status = %d\n",
|
||||
rc);
|
||||
BUG_ON(rc != 0);
|
||||
}
|
||||
|
||||
setup.dom = DOMID_SELF;
|
||||
setup.nr_frames = NR_GRANT_FRAMES;
|
||||
set_xen_guest_handle(setup.frame_list, frames);
|
||||
}
|
||||
|
||||
void fini_gnttab(void)
|
||||
{
|
||||
struct xen_remove_from_physmap xrtp;
|
||||
struct gnttab_setup_table setup;
|
||||
int i, rc;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
|
||||
for (i = 0; i < NR_GRANT_FRAMES; i++) {
|
||||
xrtp.domid = DOMID_SELF;
|
||||
xrtp.gpfn = PFN_DOWN((unsigned long)gnttab_table) + i;
|
||||
rc = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &xrtp);
|
||||
if (rc)
|
||||
printf("XENMEM_remove_from_physmap failed; status = %d\n",
|
||||
rc);
|
||||
BUG_ON(rc != 0);
|
||||
}
|
||||
|
||||
setup.dom = DOMID_SELF;
|
||||
setup.nr_frames = 0;
|
||||
}
|
||||
|
252
drivers/xen/hypervisor.c
Normal file
252
drivers/xen/hypervisor.c
Normal file
@ -0,0 +1,252 @@
|
||||
// SPDX-License-Identifier: MIT License
|
||||
/*
|
||||
* hypervisor.c
|
||||
*
|
||||
* Communication to/from hypervisor.
|
||||
*
|
||||
* Copyright (c) 2002-2003, K A Fraser
|
||||
* Copyright (c) 2005, Grzegorz Milos, gm281@cam.ac.uk,Intel Research Cambridge
|
||||
* Copyright (c) 2020, EPAM Systems Inc.
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <log.h>
|
||||
#include <memalign.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/armv8/mmu.h>
|
||||
#include <asm/xen/system.h>
|
||||
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include <xen/hvm.h>
|
||||
#include <xen/events.h>
|
||||
#include <xen/gnttab.h>
|
||||
#include <xen/xenbus.h>
|
||||
#include <xen/interface/memory.h>
|
||||
|
||||
#define active_evtchns(cpu, sh, idx) \
|
||||
((sh)->evtchn_pending[idx] & \
|
||||
~(sh)->evtchn_mask[idx])
|
||||
|
||||
int in_callback;
|
||||
|
||||
/*
|
||||
* Shared page for communicating with the hypervisor.
|
||||
* Events flags go here, for example.
|
||||
*/
|
||||
struct shared_info *HYPERVISOR_shared_info;
|
||||
|
||||
static const char *param_name(int op)
|
||||
{
|
||||
#define PARAM(x)[HVM_PARAM_##x] = #x
|
||||
static const char *const names[] = {
|
||||
PARAM(CALLBACK_IRQ),
|
||||
PARAM(STORE_PFN),
|
||||
PARAM(STORE_EVTCHN),
|
||||
PARAM(PAE_ENABLED),
|
||||
PARAM(IOREQ_PFN),
|
||||
PARAM(VPT_ALIGN),
|
||||
PARAM(CONSOLE_PFN),
|
||||
PARAM(CONSOLE_EVTCHN),
|
||||
};
|
||||
#undef PARAM
|
||||
|
||||
if (op >= ARRAY_SIZE(names))
|
||||
return "unknown";
|
||||
|
||||
if (!names[op])
|
||||
return "reserved";
|
||||
|
||||
return names[op];
|
||||
}
|
||||
|
||||
/**
|
||||
* hvm_get_parameter_maintain_dcache - function to obtain a HVM
|
||||
* parameter value.
|
||||
* @idx: HVM parameter index
|
||||
* @value: Value to fill in
|
||||
*
|
||||
* According to Xen on ARM ABI (xen/include/public/arch-arm.h):
|
||||
* all memory which is shared with other entities in the system
|
||||
* (including the hypervisor and other guests) must reside in memory
|
||||
* which is mapped as Normal Inner Write-Back Outer Write-Back
|
||||
* Inner-Shareable.
|
||||
*
|
||||
* Thus, page attributes must be equally set for all the entities
|
||||
* working with that page.
|
||||
*
|
||||
* Before MMU setup the data cache is turned off, so it means that
|
||||
* manual data cache maintenance is required, because of the
|
||||
* difference of page attributes.
|
||||
*/
|
||||
int hvm_get_parameter_maintain_dcache(int idx, uint64_t *value)
|
||||
{
|
||||
struct xen_hvm_param xhv;
|
||||
int ret;
|
||||
|
||||
invalidate_dcache_range((unsigned long)&xhv,
|
||||
(unsigned long)&xhv + sizeof(xhv));
|
||||
xhv.domid = DOMID_SELF;
|
||||
xhv.index = idx;
|
||||
invalidate_dcache_range((unsigned long)&xhv,
|
||||
(unsigned long)&xhv + sizeof(xhv));
|
||||
|
||||
ret = HYPERVISOR_hvm_op(HVMOP_get_param, &xhv);
|
||||
if (ret < 0) {
|
||||
pr_err("Cannot get hvm parameter %s (%d): %d!\n",
|
||||
param_name(idx), idx, ret);
|
||||
BUG();
|
||||
}
|
||||
invalidate_dcache_range((unsigned long)&xhv,
|
||||
(unsigned long)&xhv + sizeof(xhv));
|
||||
|
||||
*value = xhv.value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hvm_get_parameter(int idx, uint64_t *value)
|
||||
{
|
||||
struct xen_hvm_param xhv;
|
||||
int ret;
|
||||
|
||||
xhv.domid = DOMID_SELF;
|
||||
xhv.index = idx;
|
||||
ret = HYPERVISOR_hvm_op(HVMOP_get_param, &xhv);
|
||||
if (ret < 0) {
|
||||
pr_err("Cannot get hvm parameter %s (%d): %d!\n",
|
||||
param_name(idx), idx, ret);
|
||||
BUG();
|
||||
}
|
||||
|
||||
*value = xhv.value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct shared_info *map_shared_info(void *p)
|
||||
{
|
||||
struct xen_add_to_physmap xatp;
|
||||
|
||||
HYPERVISOR_shared_info = (struct shared_info *)memalign(PAGE_SIZE,
|
||||
PAGE_SIZE);
|
||||
if (!HYPERVISOR_shared_info)
|
||||
BUG();
|
||||
|
||||
xatp.domid = DOMID_SELF;
|
||||
xatp.idx = 0;
|
||||
xatp.space = XENMAPSPACE_shared_info;
|
||||
xatp.gpfn = virt_to_pfn(HYPERVISOR_shared_info);
|
||||
if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp) != 0)
|
||||
BUG();
|
||||
|
||||
return HYPERVISOR_shared_info;
|
||||
}
|
||||
|
||||
void do_hypervisor_callback(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long l1, l2, l1i, l2i;
|
||||
unsigned int port;
|
||||
int cpu = 0;
|
||||
struct shared_info *s = HYPERVISOR_shared_info;
|
||||
struct vcpu_info *vcpu_info = &s->vcpu_info[cpu];
|
||||
|
||||
in_callback = 1;
|
||||
|
||||
vcpu_info->evtchn_upcall_pending = 0;
|
||||
l1 = xchg(&vcpu_info->evtchn_pending_sel, 0);
|
||||
|
||||
while (l1 != 0) {
|
||||
l1i = __ffs(l1);
|
||||
l1 &= ~(1UL << l1i);
|
||||
|
||||
while ((l2 = active_evtchns(cpu, s, l1i)) != 0) {
|
||||
l2i = __ffs(l2);
|
||||
l2 &= ~(1UL << l2i);
|
||||
|
||||
port = (l1i * (sizeof(unsigned long) * 8)) + l2i;
|
||||
do_event(port, regs);
|
||||
}
|
||||
}
|
||||
|
||||
in_callback = 0;
|
||||
}
|
||||
|
||||
void force_evtchn_callback(void)
|
||||
{
|
||||
#ifdef XEN_HAVE_PV_UPCALL_MASK
|
||||
int save;
|
||||
#endif
|
||||
struct vcpu_info *vcpu;
|
||||
|
||||
vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()];
|
||||
#ifdef XEN_HAVE_PV_UPCALL_MASK
|
||||
save = vcpu->evtchn_upcall_mask;
|
||||
#endif
|
||||
|
||||
while (vcpu->evtchn_upcall_pending) {
|
||||
#ifdef XEN_HAVE_PV_UPCALL_MASK
|
||||
vcpu->evtchn_upcall_mask = 1;
|
||||
#endif
|
||||
do_hypervisor_callback(NULL);
|
||||
#ifdef XEN_HAVE_PV_UPCALL_MASK
|
||||
vcpu->evtchn_upcall_mask = save;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
void mask_evtchn(uint32_t port)
|
||||
{
|
||||
struct shared_info *s = HYPERVISOR_shared_info;
|
||||
|
||||
synch_set_bit(port, &s->evtchn_mask[0]);
|
||||
}
|
||||
|
||||
void unmask_evtchn(uint32_t port)
|
||||
{
|
||||
struct shared_info *s = HYPERVISOR_shared_info;
|
||||
struct vcpu_info *vcpu_info = &s->vcpu_info[smp_processor_id()];
|
||||
|
||||
synch_clear_bit(port, &s->evtchn_mask[0]);
|
||||
|
||||
/*
|
||||
* Just like a real IO-APIC we 'lose the interrupt edge' if the
|
||||
* channel is masked.
|
||||
*/
|
||||
if (synch_test_bit(port, &s->evtchn_pending[0]) &&
|
||||
!synch_test_and_set_bit(port / (sizeof(unsigned long) * 8),
|
||||
&vcpu_info->evtchn_pending_sel)) {
|
||||
vcpu_info->evtchn_upcall_pending = 1;
|
||||
#ifdef XEN_HAVE_PV_UPCALL_MASK
|
||||
if (!vcpu_info->evtchn_upcall_mask)
|
||||
#endif
|
||||
force_evtchn_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void clear_evtchn(uint32_t port)
|
||||
{
|
||||
struct shared_info *s = HYPERVISOR_shared_info;
|
||||
|
||||
synch_clear_bit(port, &s->evtchn_pending[0]);
|
||||
}
|
||||
|
||||
void xen_init(void)
|
||||
{
|
||||
debug("%s\n", __func__);
|
||||
|
||||
map_shared_info(NULL);
|
||||
init_events();
|
||||
init_xenbus();
|
||||
init_gnttab();
|
||||
}
|
||||
|
||||
void xen_fini(void)
|
||||
{
|
||||
debug("%s\n", __func__);
|
||||
|
||||
fini_gnttab();
|
||||
fini_xenbus();
|
||||
fini_events();
|
||||
}
|
867
drivers/xen/pvblock.c
Normal file
867
drivers/xen/pvblock.c
Normal file
@ -0,0 +1,867 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) 2007-2008 Samuel Thibault.
|
||||
* (C) Copyright 2020 EPAM Systems Inc.
|
||||
*/
|
||||
#include <blk.h>
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <malloc.h>
|
||||
#include <part.h>
|
||||
|
||||
#include <asm/armv8/mmu.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/xen/system.h>
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include <xen/events.h>
|
||||
#include <xen/gnttab.h>
|
||||
#include <xen/hvm.h>
|
||||
#include <xen/xenbus.h>
|
||||
|
||||
#include <xen/interface/io/ring.h>
|
||||
#include <xen/interface/io/blkif.h>
|
||||
#include <xen/interface/io/protocols.h>
|
||||
|
||||
#define DRV_NAME "pvblock"
|
||||
#define DRV_NAME_BLK "pvblock_blk"
|
||||
|
||||
#define O_RDONLY 00
|
||||
#define O_RDWR 02
|
||||
#define WAIT_RING_TO_MS 10
|
||||
|
||||
struct blkfront_info {
|
||||
u64 sectors;
|
||||
unsigned int sector_size;
|
||||
int mode;
|
||||
int info;
|
||||
int barrier;
|
||||
int flush;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct blkfront_dev - Struct representing blkfront device
|
||||
* @dom: Domain id
|
||||
* @ring: Front_ring structure
|
||||
* @ring_ref: The grant reference, allowing us to grant access
|
||||
* to the ring to the other end/domain
|
||||
* @evtchn: Event channel used to signal ring events
|
||||
* @handle: Events handle
|
||||
* @nodename: Device XenStore path in format "device/vbd/" + @devid
|
||||
* @backend: Backend XenStore path
|
||||
* @info: Private data
|
||||
* @devid: Device id
|
||||
*/
|
||||
struct blkfront_dev {
|
||||
domid_t dom;
|
||||
|
||||
struct blkif_front_ring ring;
|
||||
grant_ref_t ring_ref;
|
||||
evtchn_port_t evtchn;
|
||||
blkif_vdev_t handle;
|
||||
|
||||
char *nodename;
|
||||
char *backend;
|
||||
struct blkfront_info info;
|
||||
unsigned int devid;
|
||||
u8 *bounce_buffer;
|
||||
};
|
||||
|
||||
struct blkfront_platdata {
|
||||
unsigned int devid;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct blkfront_aiocb - AIO сontrol block
|
||||
* @aio_dev: Blockfront device
|
||||
* @aio_buf: Memory buffer, which must be sector-aligned for
|
||||
* @aio_dev sector
|
||||
* @aio_nbytes: Size of AIO, which must be less than @aio_dev
|
||||
* sector-sized amounts
|
||||
* @aio_offset: Offset, which must not go beyond @aio_dev
|
||||
* sector-aligned location
|
||||
* @data: Data used to receiving response from ring
|
||||
* @gref: Array of grant references
|
||||
* @n: Number of segments
|
||||
* @aio_cb: Represents one I/O request.
|
||||
*/
|
||||
struct blkfront_aiocb {
|
||||
struct blkfront_dev *aio_dev;
|
||||
u8 *aio_buf;
|
||||
size_t aio_nbytes;
|
||||
off_t aio_offset;
|
||||
void *data;
|
||||
|
||||
grant_ref_t gref[BLKIF_MAX_SEGMENTS_PER_REQUEST];
|
||||
int n;
|
||||
|
||||
void (*aio_cb)(struct blkfront_aiocb *aiocb, int ret);
|
||||
};
|
||||
|
||||
static void blkfront_sync(struct blkfront_dev *dev);
|
||||
|
||||
static void free_blkfront(struct blkfront_dev *dev)
|
||||
{
|
||||
mask_evtchn(dev->evtchn);
|
||||
free(dev->backend);
|
||||
|
||||
gnttab_end_access(dev->ring_ref);
|
||||
free(dev->ring.sring);
|
||||
|
||||
unbind_evtchn(dev->evtchn);
|
||||
|
||||
free(dev->bounce_buffer);
|
||||
free(dev->nodename);
|
||||
free(dev);
|
||||
}
|
||||
|
||||
static int init_blkfront(unsigned int devid, struct blkfront_dev *dev)
|
||||
{
|
||||
xenbus_transaction_t xbt;
|
||||
char *err = NULL;
|
||||
char *message = NULL;
|
||||
struct blkif_sring *s;
|
||||
int retry = 0;
|
||||
char *msg = NULL;
|
||||
char *c;
|
||||
char nodename[32];
|
||||
char path[ARRAY_SIZE(nodename) + strlen("/backend-id") + 1];
|
||||
|
||||
sprintf(nodename, "device/vbd/%d", devid);
|
||||
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
dev->nodename = strdup(nodename);
|
||||
dev->devid = devid;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/backend-id", nodename);
|
||||
dev->dom = xenbus_read_integer(path);
|
||||
evtchn_alloc_unbound(dev->dom, NULL, dev, &dev->evtchn);
|
||||
|
||||
s = (struct blkif_sring *)memalign(PAGE_SIZE, PAGE_SIZE);
|
||||
if (!s) {
|
||||
printf("Failed to allocate shared ring\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
SHARED_RING_INIT(s);
|
||||
FRONT_RING_INIT(&dev->ring, s, PAGE_SIZE);
|
||||
|
||||
dev->ring_ref = gnttab_grant_access(dev->dom, virt_to_pfn(s), 0);
|
||||
|
||||
again:
|
||||
err = xenbus_transaction_start(&xbt);
|
||||
if (err) {
|
||||
printf("starting transaction\n");
|
||||
free(err);
|
||||
}
|
||||
|
||||
err = xenbus_printf(xbt, nodename, "ring-ref", "%u", dev->ring_ref);
|
||||
if (err) {
|
||||
message = "writing ring-ref";
|
||||
goto abort_transaction;
|
||||
}
|
||||
err = xenbus_printf(xbt, nodename, "event-channel", "%u", dev->evtchn);
|
||||
if (err) {
|
||||
message = "writing event-channel";
|
||||
goto abort_transaction;
|
||||
}
|
||||
err = xenbus_printf(xbt, nodename, "protocol", "%s",
|
||||
XEN_IO_PROTO_ABI_NATIVE);
|
||||
if (err) {
|
||||
message = "writing protocol";
|
||||
goto abort_transaction;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s/state", nodename);
|
||||
err = xenbus_switch_state(xbt, path, XenbusStateConnected);
|
||||
if (err) {
|
||||
message = "switching state";
|
||||
goto abort_transaction;
|
||||
}
|
||||
|
||||
err = xenbus_transaction_end(xbt, 0, &retry);
|
||||
free(err);
|
||||
if (retry) {
|
||||
goto again;
|
||||
printf("completing transaction\n");
|
||||
}
|
||||
|
||||
goto done;
|
||||
|
||||
abort_transaction:
|
||||
free(err);
|
||||
err = xenbus_transaction_end(xbt, 1, &retry);
|
||||
printf("Abort transaction %s\n", message);
|
||||
goto error;
|
||||
|
||||
done:
|
||||
snprintf(path, sizeof(path), "%s/backend", nodename);
|
||||
msg = xenbus_read(XBT_NIL, path, &dev->backend);
|
||||
if (msg) {
|
||||
printf("Error %s when reading the backend path %s\n",
|
||||
msg, path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
dev->handle = strtoul(strrchr(nodename, '/') + 1, NULL, 0);
|
||||
|
||||
{
|
||||
XenbusState state;
|
||||
char path[strlen(dev->backend) +
|
||||
strlen("/feature-flush-cache") + 1];
|
||||
|
||||
snprintf(path, sizeof(path), "%s/mode", dev->backend);
|
||||
msg = xenbus_read(XBT_NIL, path, &c);
|
||||
if (msg) {
|
||||
printf("Error %s when reading the mode\n", msg);
|
||||
goto error;
|
||||
}
|
||||
if (*c == 'w')
|
||||
dev->info.mode = O_RDWR;
|
||||
else
|
||||
dev->info.mode = O_RDONLY;
|
||||
free(c);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/state", dev->backend);
|
||||
|
||||
msg = NULL;
|
||||
state = xenbus_read_integer(path);
|
||||
while (!msg && state < XenbusStateConnected)
|
||||
msg = xenbus_wait_for_state_change(path, &state);
|
||||
if (msg || state != XenbusStateConnected) {
|
||||
printf("backend not available, state=%d\n", state);
|
||||
goto error;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s/info", dev->backend);
|
||||
dev->info.info = xenbus_read_integer(path);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/sectors", dev->backend);
|
||||
/*
|
||||
* FIXME: read_integer returns an int, so disk size
|
||||
* limited to 1TB for now
|
||||
*/
|
||||
dev->info.sectors = xenbus_read_integer(path);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/sector-size", dev->backend);
|
||||
dev->info.sector_size = xenbus_read_integer(path);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/feature-barrier",
|
||||
dev->backend);
|
||||
dev->info.barrier = xenbus_read_integer(path);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/feature-flush-cache",
|
||||
dev->backend);
|
||||
dev->info.flush = xenbus_read_integer(path);
|
||||
}
|
||||
unmask_evtchn(dev->evtchn);
|
||||
|
||||
dev->bounce_buffer = memalign(dev->info.sector_size,
|
||||
dev->info.sector_size);
|
||||
if (!dev->bounce_buffer) {
|
||||
printf("Failed to allocate bouncing buffer\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
debug("%llu sectors of %u bytes, bounce buffer at %p\n",
|
||||
dev->info.sectors, dev->info.sector_size,
|
||||
dev->bounce_buffer);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
free(msg);
|
||||
free(err);
|
||||
free_blkfront(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void shutdown_blkfront(struct blkfront_dev *dev)
|
||||
{
|
||||
char *err = NULL, *err2;
|
||||
XenbusState state;
|
||||
|
||||
char path[strlen(dev->backend) + strlen("/state") + 1];
|
||||
char nodename[strlen(dev->nodename) + strlen("/event-channel") + 1];
|
||||
|
||||
debug("Close " DRV_NAME ", device ID %d\n", dev->devid);
|
||||
|
||||
blkfront_sync(dev);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/state", dev->backend);
|
||||
snprintf(nodename, sizeof(nodename), "%s/state", dev->nodename);
|
||||
|
||||
if ((err = xenbus_switch_state(XBT_NIL, nodename,
|
||||
XenbusStateClosing)) != NULL) {
|
||||
printf("%s: error changing state to %d: %s\n", __func__,
|
||||
XenbusStateClosing, err);
|
||||
goto close;
|
||||
}
|
||||
|
||||
state = xenbus_read_integer(path);
|
||||
while (!err && state < XenbusStateClosing)
|
||||
err = xenbus_wait_for_state_change(path, &state);
|
||||
free(err);
|
||||
|
||||
if ((err = xenbus_switch_state(XBT_NIL, nodename,
|
||||
XenbusStateClosed)) != NULL) {
|
||||
printf("%s: error changing state to %d: %s\n", __func__,
|
||||
XenbusStateClosed, err);
|
||||
goto close;
|
||||
}
|
||||
|
||||
state = xenbus_read_integer(path);
|
||||
while (state < XenbusStateClosed) {
|
||||
err = xenbus_wait_for_state_change(path, &state);
|
||||
free(err);
|
||||
}
|
||||
|
||||
if ((err = xenbus_switch_state(XBT_NIL, nodename,
|
||||
XenbusStateInitialising)) != NULL) {
|
||||
printf("%s: error changing state to %d: %s\n", __func__,
|
||||
XenbusStateInitialising, err);
|
||||
goto close;
|
||||
}
|
||||
|
||||
state = xenbus_read_integer(path);
|
||||
while (!err &&
|
||||
(state < XenbusStateInitWait || state >= XenbusStateClosed))
|
||||
err = xenbus_wait_for_state_change(path, &state);
|
||||
|
||||
close:
|
||||
free(err);
|
||||
|
||||
snprintf(nodename, sizeof(nodename), "%s/ring-ref", dev->nodename);
|
||||
err2 = xenbus_rm(XBT_NIL, nodename);
|
||||
free(err2);
|
||||
snprintf(nodename, sizeof(nodename), "%s/event-channel", dev->nodename);
|
||||
err2 = xenbus_rm(XBT_NIL, nodename);
|
||||
free(err2);
|
||||
|
||||
if (!err)
|
||||
free_blkfront(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* blkfront_aio_poll() - AIO polling function.
|
||||
* @dev: Blkfront device
|
||||
*
|
||||
* Here we receive response from the ring and check its status. This happens
|
||||
* until we read all data from the ring. We read the data from consumed pointer
|
||||
* to the response pointer. Then increase consumed pointer to make it clear that
|
||||
* the data has been read.
|
||||
*
|
||||
* Return: Number of consumed bytes.
|
||||
*/
|
||||
static int blkfront_aio_poll(struct blkfront_dev *dev)
|
||||
{
|
||||
RING_IDX rp, cons;
|
||||
struct blkif_response *rsp;
|
||||
int more;
|
||||
int nr_consumed;
|
||||
|
||||
moretodo:
|
||||
rp = dev->ring.sring->rsp_prod;
|
||||
rmb(); /* Ensure we see queued responses up to 'rp'. */
|
||||
cons = dev->ring.rsp_cons;
|
||||
|
||||
nr_consumed = 0;
|
||||
while ((cons != rp)) {
|
||||
struct blkfront_aiocb *aiocbp;
|
||||
int status;
|
||||
|
||||
rsp = RING_GET_RESPONSE(&dev->ring, cons);
|
||||
nr_consumed++;
|
||||
|
||||
aiocbp = (void *)(uintptr_t)rsp->id;
|
||||
status = rsp->status;
|
||||
|
||||
switch (rsp->operation) {
|
||||
case BLKIF_OP_READ:
|
||||
case BLKIF_OP_WRITE:
|
||||
{
|
||||
int j;
|
||||
|
||||
if (status != BLKIF_RSP_OKAY)
|
||||
printf("%s error %d on %s at offset %llu, num bytes %llu\n",
|
||||
rsp->operation == BLKIF_OP_READ ?
|
||||
"read" : "write",
|
||||
status, aiocbp->aio_dev->nodename,
|
||||
(unsigned long long)aiocbp->aio_offset,
|
||||
(unsigned long long)aiocbp->aio_nbytes);
|
||||
|
||||
for (j = 0; j < aiocbp->n; j++)
|
||||
gnttab_end_access(aiocbp->gref[j]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BLKIF_OP_WRITE_BARRIER:
|
||||
if (status != BLKIF_RSP_OKAY)
|
||||
printf("write barrier error %d\n", status);
|
||||
break;
|
||||
case BLKIF_OP_FLUSH_DISKCACHE:
|
||||
if (status != BLKIF_RSP_OKAY)
|
||||
printf("flush error %d\n", status);
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("unrecognized block operation %d response (status %d)\n",
|
||||
rsp->operation, status);
|
||||
break;
|
||||
}
|
||||
|
||||
dev->ring.rsp_cons = ++cons;
|
||||
/* Nota: callback frees aiocbp itself */
|
||||
if (aiocbp && aiocbp->aio_cb)
|
||||
aiocbp->aio_cb(aiocbp, status ? -EIO : 0);
|
||||
if (dev->ring.rsp_cons != cons)
|
||||
/* We reentered, we must not continue here */
|
||||
break;
|
||||
}
|
||||
|
||||
RING_FINAL_CHECK_FOR_RESPONSES(&dev->ring, more);
|
||||
if (more)
|
||||
goto moretodo;
|
||||
|
||||
return nr_consumed;
|
||||
}
|
||||
|
||||
static void blkfront_wait_slot(struct blkfront_dev *dev)
|
||||
{
|
||||
/* Wait for a slot */
|
||||
if (RING_FULL(&dev->ring)) {
|
||||
while (true) {
|
||||
blkfront_aio_poll(dev);
|
||||
if (!RING_FULL(&dev->ring))
|
||||
break;
|
||||
wait_event_timeout(NULL, !RING_FULL(&dev->ring),
|
||||
WAIT_RING_TO_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* blkfront_aio_poll() - Issue an aio.
|
||||
* @aiocbp: AIO control block structure
|
||||
* @write: Describes is it read or write operation
|
||||
* 0 - read
|
||||
* 1 - write
|
||||
*
|
||||
* We check whether the AIO parameters meet the requirements of the device.
|
||||
* Then receive request from ring and define its arguments. After this we
|
||||
* grant access to the grant references. The last step is notifying about AIO
|
||||
* via event channel.
|
||||
*/
|
||||
static void blkfront_aio(struct blkfront_aiocb *aiocbp, int write)
|
||||
{
|
||||
struct blkfront_dev *dev = aiocbp->aio_dev;
|
||||
struct blkif_request *req;
|
||||
RING_IDX i;
|
||||
int notify;
|
||||
int n, j;
|
||||
uintptr_t start, end;
|
||||
|
||||
/* Can't io at non-sector-aligned location */
|
||||
BUG_ON(aiocbp->aio_offset & (dev->info.sector_size - 1));
|
||||
/* Can't io non-sector-sized amounts */
|
||||
BUG_ON(aiocbp->aio_nbytes & (dev->info.sector_size - 1));
|
||||
/* Can't io non-sector-aligned buffer */
|
||||
BUG_ON(((uintptr_t)aiocbp->aio_buf & (dev->info.sector_size - 1)));
|
||||
|
||||
start = (uintptr_t)aiocbp->aio_buf & PAGE_MASK;
|
||||
end = ((uintptr_t)aiocbp->aio_buf + aiocbp->aio_nbytes +
|
||||
PAGE_SIZE - 1) & PAGE_MASK;
|
||||
n = (end - start) / PAGE_SIZE;
|
||||
aiocbp->n = n;
|
||||
|
||||
BUG_ON(n > BLKIF_MAX_SEGMENTS_PER_REQUEST);
|
||||
|
||||
blkfront_wait_slot(dev);
|
||||
i = dev->ring.req_prod_pvt;
|
||||
req = RING_GET_REQUEST(&dev->ring, i);
|
||||
|
||||
req->operation = write ? BLKIF_OP_WRITE : BLKIF_OP_READ;
|
||||
req->nr_segments = n;
|
||||
req->handle = dev->handle;
|
||||
req->id = (uintptr_t)aiocbp;
|
||||
req->sector_number = aiocbp->aio_offset / dev->info.sector_size;
|
||||
|
||||
for (j = 0; j < n; j++) {
|
||||
req->seg[j].first_sect = 0;
|
||||
req->seg[j].last_sect = PAGE_SIZE / dev->info.sector_size - 1;
|
||||
}
|
||||
req->seg[0].first_sect = ((uintptr_t)aiocbp->aio_buf & ~PAGE_MASK) /
|
||||
dev->info.sector_size;
|
||||
req->seg[n - 1].last_sect = (((uintptr_t)aiocbp->aio_buf +
|
||||
aiocbp->aio_nbytes - 1) & ~PAGE_MASK) / dev->info.sector_size;
|
||||
for (j = 0; j < n; j++) {
|
||||
uintptr_t data = start + j * PAGE_SIZE;
|
||||
|
||||
if (!write) {
|
||||
/* Trigger CoW if needed */
|
||||
*(char *)(data + (req->seg[j].first_sect *
|
||||
dev->info.sector_size)) = 0;
|
||||
barrier();
|
||||
}
|
||||
req->seg[j].gref = gnttab_grant_access(dev->dom,
|
||||
virt_to_pfn((void *)data),
|
||||
write);
|
||||
aiocbp->gref[j] = req->seg[j].gref;
|
||||
}
|
||||
|
||||
dev->ring.req_prod_pvt = i + 1;
|
||||
|
||||
wmb();
|
||||
RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->ring, notify);
|
||||
|
||||
if (notify)
|
||||
notify_remote_via_evtchn(dev->evtchn);
|
||||
}
|
||||
|
||||
static void blkfront_aio_cb(struct blkfront_aiocb *aiocbp, int ret)
|
||||
{
|
||||
aiocbp->data = (void *)1;
|
||||
aiocbp->aio_cb = NULL;
|
||||
}
|
||||
|
||||
static void blkfront_io(struct blkfront_aiocb *aiocbp, int write)
|
||||
{
|
||||
aiocbp->aio_cb = blkfront_aio_cb;
|
||||
blkfront_aio(aiocbp, write);
|
||||
aiocbp->data = NULL;
|
||||
|
||||
while (true) {
|
||||
blkfront_aio_poll(aiocbp->aio_dev);
|
||||
if (aiocbp->data)
|
||||
break;
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
|
||||
static void blkfront_push_operation(struct blkfront_dev *dev, u8 op,
|
||||
uint64_t id)
|
||||
{
|
||||
struct blkif_request *req;
|
||||
int notify, i;
|
||||
|
||||
blkfront_wait_slot(dev);
|
||||
i = dev->ring.req_prod_pvt;
|
||||
req = RING_GET_REQUEST(&dev->ring, i);
|
||||
req->operation = op;
|
||||
req->nr_segments = 0;
|
||||
req->handle = dev->handle;
|
||||
req->id = id;
|
||||
req->sector_number = 0;
|
||||
dev->ring.req_prod_pvt = i + 1;
|
||||
wmb();
|
||||
RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->ring, notify);
|
||||
if (notify)
|
||||
notify_remote_via_evtchn(dev->evtchn);
|
||||
}
|
||||
|
||||
static void blkfront_sync(struct blkfront_dev *dev)
|
||||
{
|
||||
if (dev->info.mode == O_RDWR) {
|
||||
if (dev->info.barrier == 1)
|
||||
blkfront_push_operation(dev,
|
||||
BLKIF_OP_WRITE_BARRIER, 0);
|
||||
|
||||
if (dev->info.flush == 1)
|
||||
blkfront_push_operation(dev,
|
||||
BLKIF_OP_FLUSH_DISKCACHE, 0);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
blkfront_aio_poll(dev);
|
||||
if (RING_FREE_REQUESTS(&dev->ring) == RING_SIZE(&dev->ring))
|
||||
break;
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pvblock_iop() - Issue an aio.
|
||||
* @udev: Pvblock device
|
||||
* @blknr: Block number to read from / write to
|
||||
* @blkcnt: Amount of blocks to read / write
|
||||
* @buffer: Memory buffer with data to be read / write
|
||||
* @write: Describes is it read or write operation
|
||||
* 0 - read
|
||||
* 1 - write
|
||||
*
|
||||
* Depending on the operation - reading or writing, data is read / written from the
|
||||
* specified address (@buffer) to the sector (@blknr).
|
||||
*/
|
||||
static ulong pvblock_iop(struct udevice *udev, lbaint_t blknr,
|
||||
lbaint_t blkcnt, void *buffer, int write)
|
||||
{
|
||||
struct blkfront_dev *blk_dev = dev_get_priv(udev);
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(udev);
|
||||
struct blkfront_aiocb aiocb;
|
||||
lbaint_t blocks_todo;
|
||||
bool unaligned;
|
||||
|
||||
if (blkcnt == 0)
|
||||
return 0;
|
||||
|
||||
if ((blknr + blkcnt) > desc->lba) {
|
||||
printf(DRV_NAME ": block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
|
||||
blknr + blkcnt, desc->lba);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unaligned = (uintptr_t)buffer & (blk_dev->info.sector_size - 1);
|
||||
|
||||
aiocb.aio_dev = blk_dev;
|
||||
aiocb.aio_offset = blknr * desc->blksz;
|
||||
aiocb.aio_cb = NULL;
|
||||
aiocb.data = NULL;
|
||||
blocks_todo = blkcnt;
|
||||
do {
|
||||
aiocb.aio_buf = unaligned ? blk_dev->bounce_buffer : buffer;
|
||||
|
||||
if (write && unaligned)
|
||||
memcpy(blk_dev->bounce_buffer, buffer, desc->blksz);
|
||||
|
||||
aiocb.aio_nbytes = unaligned ? desc->blksz :
|
||||
min((size_t)(BLKIF_MAX_SEGMENTS_PER_REQUEST * PAGE_SIZE),
|
||||
(size_t)(blocks_todo * desc->blksz));
|
||||
|
||||
blkfront_io(&aiocb, write);
|
||||
|
||||
if (!write && unaligned)
|
||||
memcpy(buffer, blk_dev->bounce_buffer, desc->blksz);
|
||||
|
||||
aiocb.aio_offset += aiocb.aio_nbytes;
|
||||
buffer += aiocb.aio_nbytes;
|
||||
blocks_todo -= aiocb.aio_nbytes / desc->blksz;
|
||||
} while (blocks_todo > 0);
|
||||
|
||||
return blkcnt;
|
||||
}
|
||||
|
||||
ulong pvblock_blk_read(struct udevice *udev, lbaint_t blknr, lbaint_t blkcnt,
|
||||
void *buffer)
|
||||
{
|
||||
return pvblock_iop(udev, blknr, blkcnt, buffer, 0);
|
||||
}
|
||||
|
||||
ulong pvblock_blk_write(struct udevice *udev, lbaint_t blknr, lbaint_t blkcnt,
|
||||
const void *buffer)
|
||||
{
|
||||
return pvblock_iop(udev, blknr, blkcnt, (void *)buffer, 1);
|
||||
}
|
||||
|
||||
static int pvblock_blk_bind(struct udevice *udev)
|
||||
{
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(udev);
|
||||
int devnum;
|
||||
|
||||
desc->if_type = IF_TYPE_PVBLOCK;
|
||||
/*
|
||||
* Initialize the devnum to -ENODEV. This is to make sure that
|
||||
* blk_next_free_devnum() works as expected, since the default
|
||||
* value 0 is a valid devnum.
|
||||
*/
|
||||
desc->devnum = -ENODEV;
|
||||
devnum = blk_next_free_devnum(IF_TYPE_PVBLOCK);
|
||||
if (devnum < 0)
|
||||
return devnum;
|
||||
desc->devnum = devnum;
|
||||
desc->part_type = PART_TYPE_UNKNOWN;
|
||||
desc->bdev = udev;
|
||||
|
||||
strncpy(desc->vendor, "Xen", sizeof(desc->vendor));
|
||||
strncpy(desc->revision, "1", sizeof(desc->revision));
|
||||
strncpy(desc->product, "Virtual disk", sizeof(desc->product));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pvblock_blk_probe(struct udevice *udev)
|
||||
{
|
||||
struct blkfront_dev *blk_dev = dev_get_priv(udev);
|
||||
struct blkfront_platdata *platdata = dev_get_platdata(udev);
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(udev);
|
||||
int ret, devid;
|
||||
|
||||
devid = platdata->devid;
|
||||
free(platdata);
|
||||
|
||||
ret = init_blkfront(devid, blk_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
desc->blksz = blk_dev->info.sector_size;
|
||||
desc->lba = blk_dev->info.sectors;
|
||||
desc->log2blksz = LOG2(blk_dev->info.sector_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pvblock_blk_remove(struct udevice *udev)
|
||||
{
|
||||
struct blkfront_dev *blk_dev = dev_get_priv(udev);
|
||||
|
||||
shutdown_blkfront(blk_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct blk_ops pvblock_blk_ops = {
|
||||
.read = pvblock_blk_read,
|
||||
.write = pvblock_blk_write,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pvblock_blk) = {
|
||||
.name = DRV_NAME_BLK,
|
||||
.id = UCLASS_BLK,
|
||||
.ops = &pvblock_blk_ops,
|
||||
.bind = pvblock_blk_bind,
|
||||
.probe = pvblock_blk_probe,
|
||||
.remove = pvblock_blk_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct blkfront_dev),
|
||||
.flags = DM_FLAG_OS_PREPARE,
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Para-virtual block device class
|
||||
*******************************************************************************/
|
||||
|
||||
typedef int (*enum_vbd_callback)(struct udevice *parent, unsigned int devid);
|
||||
|
||||
static int on_new_vbd(struct udevice *parent, unsigned int devid)
|
||||
{
|
||||
struct driver_info info;
|
||||
struct udevice *udev;
|
||||
struct blkfront_platdata *platdata;
|
||||
int ret;
|
||||
|
||||
debug("New " DRV_NAME_BLK ", device ID %d\n", devid);
|
||||
|
||||
platdata = malloc(sizeof(struct blkfront_platdata));
|
||||
if (!platdata) {
|
||||
printf("Failed to allocate platform data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platdata->devid = devid;
|
||||
|
||||
info.name = DRV_NAME_BLK;
|
||||
info.platdata = platdata;
|
||||
|
||||
ret = device_bind_by_name(parent, false, &info, &udev);
|
||||
if (ret < 0) {
|
||||
printf("Failed to bind " DRV_NAME_BLK " to device with ID %d, ret: %d\n",
|
||||
devid, ret);
|
||||
free(platdata);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xenbus_enumerate_vbd(struct udevice *udev, enum_vbd_callback clb)
|
||||
{
|
||||
char **dirs, *msg;
|
||||
int i, ret;
|
||||
|
||||
msg = xenbus_ls(XBT_NIL, "device/vbd", &dirs);
|
||||
if (msg) {
|
||||
printf("Failed to read device/vbd directory: %s\n", msg);
|
||||
free(msg);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; dirs[i]; i++) {
|
||||
int devid;
|
||||
|
||||
sscanf(dirs[i], "%d", &devid);
|
||||
ret = clb(udev, devid);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
free(dirs[i]);
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
for (; dirs[i]; i++)
|
||||
free(dirs[i]);
|
||||
free(dirs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void print_pvblock_devices(void)
|
||||
{
|
||||
struct udevice *udev;
|
||||
bool first = true;
|
||||
const char *class_name;
|
||||
|
||||
class_name = uclass_get_name(UCLASS_PVBLOCK);
|
||||
for (blk_first_device(IF_TYPE_PVBLOCK, &udev); udev;
|
||||
blk_next_device(&udev), first = false) {
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(udev);
|
||||
|
||||
if (!first)
|
||||
puts(", ");
|
||||
printf("%s: %d", class_name, desc->devnum);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void pvblock_init(void)
|
||||
{
|
||||
struct driver_info info;
|
||||
struct udevice *udev;
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* At this point Xen drivers have already initialized,
|
||||
* so we can instantiate the class driver and enumerate
|
||||
* virtual block devices.
|
||||
*/
|
||||
info.name = DRV_NAME;
|
||||
ret = device_bind_by_name(gd->dm_root, false, &info, &udev);
|
||||
if (ret < 0)
|
||||
printf("Failed to bind " DRV_NAME ", ret: %d\n", ret);
|
||||
|
||||
/* Bootstrap virtual block devices class driver */
|
||||
ret = uclass_get(UCLASS_PVBLOCK, &uc);
|
||||
if (ret)
|
||||
return;
|
||||
uclass_foreach_dev_probe(UCLASS_PVBLOCK, udev);
|
||||
|
||||
print_pvblock_devices();
|
||||
}
|
||||
|
||||
static int pvblock_probe(struct udevice *udev)
|
||||
{
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
if (xenbus_enumerate_vbd(udev, on_new_vbd) < 0)
|
||||
return -ENODEV;
|
||||
|
||||
ret = uclass_get(UCLASS_BLK, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
uclass_foreach_dev_probe(UCLASS_BLK, udev) {
|
||||
if (_ret)
|
||||
return _ret;
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(pvblock_drv) = {
|
||||
.name = DRV_NAME,
|
||||
.id = UCLASS_PVBLOCK,
|
||||
.probe = pvblock_probe,
|
||||
};
|
||||
|
||||
UCLASS_DRIVER(pvblock) = {
|
||||
.name = DRV_NAME,
|
||||
.id = UCLASS_PVBLOCK,
|
||||
};
|
557
drivers/xen/xenbus.c
Normal file
557
drivers/xen/xenbus.c
Normal file
@ -0,0 +1,557 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* (C) 2006 - Cambridge University
|
||||
* (C) 2020 - EPAM Systems Inc.
|
||||
*
|
||||
* File: xenbus.c [1]
|
||||
* Author: Steven Smith (sos22@cam.ac.uk)
|
||||
* Changes: Grzegorz Milos (gm281@cam.ac.uk)
|
||||
* Changes: John D. Ramsdell
|
||||
*
|
||||
* Date: Jun 2006, changes Aug 2006
|
||||
*
|
||||
* Description: Minimal implementation of xenbus
|
||||
*
|
||||
* [1] - http://xenbits.xen.org/gitweb/?p=mini-os.git;a=summary
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <log.h>
|
||||
|
||||
#include <asm/armv8/mmu.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/xen/system.h>
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include <xen/events.h>
|
||||
#include <xen/hvm.h>
|
||||
#include <xen/xenbus.h>
|
||||
|
||||
#include <xen/interface/io/xs_wire.h>
|
||||
|
||||
#define map_frame_virt(v) (v << PAGE_SHIFT)
|
||||
|
||||
#define SCNd16 "d"
|
||||
|
||||
/* Wait for reply time out, ms */
|
||||
#define WAIT_XENBUS_TO_MS 5000
|
||||
/* Polling time out, ms */
|
||||
#define WAIT_XENBUS_POLL_TO_MS 1
|
||||
|
||||
static struct xenstore_domain_interface *xenstore_buf;
|
||||
|
||||
static char *errmsg(struct xsd_sockmsg *rep);
|
||||
|
||||
u32 xenbus_evtchn;
|
||||
|
||||
struct write_req {
|
||||
const void *data;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
static void memcpy_from_ring(const void *r, void *d, int off, int len)
|
||||
{
|
||||
int c1, c2;
|
||||
const char *ring = r;
|
||||
char *dest = d;
|
||||
|
||||
c1 = min(len, XENSTORE_RING_SIZE - off);
|
||||
c2 = len - c1;
|
||||
memcpy(dest, ring + off, c1);
|
||||
memcpy(dest + c1, ring, c2);
|
||||
}
|
||||
|
||||
/**
|
||||
* xenbus_get_reply() - Receive reply from xenbus
|
||||
* @req_reply: reply message structure
|
||||
*
|
||||
* Wait for reply message event from the ring and copy received message
|
||||
* to input xsd_sockmsg structure. Repeat until full reply is
|
||||
* proceeded.
|
||||
*
|
||||
* Return: false - timeout
|
||||
* true - reply is received
|
||||
*/
|
||||
static bool xenbus_get_reply(struct xsd_sockmsg **req_reply)
|
||||
{
|
||||
struct xsd_sockmsg msg;
|
||||
unsigned int prod = xenstore_buf->rsp_prod;
|
||||
|
||||
again:
|
||||
if (!wait_event_timeout(NULL, prod != xenstore_buf->rsp_prod,
|
||||
WAIT_XENBUS_TO_MS)) {
|
||||
printk("%s: wait_event timeout\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
prod = xenstore_buf->rsp_prod;
|
||||
if (xenstore_buf->rsp_prod - xenstore_buf->rsp_cons < sizeof(msg))
|
||||
goto again;
|
||||
|
||||
rmb();
|
||||
memcpy_from_ring(xenstore_buf->rsp, &msg,
|
||||
MASK_XENSTORE_IDX(xenstore_buf->rsp_cons),
|
||||
sizeof(msg));
|
||||
|
||||
if (xenstore_buf->rsp_prod - xenstore_buf->rsp_cons < sizeof(msg) + msg.len)
|
||||
goto again;
|
||||
|
||||
/* We do not support and expect any Xen bus wathes. */
|
||||
BUG_ON(msg.type == XS_WATCH_EVENT);
|
||||
|
||||
*req_reply = malloc(sizeof(msg) + msg.len);
|
||||
memcpy_from_ring(xenstore_buf->rsp, *req_reply,
|
||||
MASK_XENSTORE_IDX(xenstore_buf->rsp_cons),
|
||||
msg.len + sizeof(msg));
|
||||
mb();
|
||||
xenstore_buf->rsp_cons += msg.len + sizeof(msg);
|
||||
|
||||
wmb();
|
||||
notify_remote_via_evtchn(xenbus_evtchn);
|
||||
return true;
|
||||
}
|
||||
|
||||
char *xenbus_switch_state(xenbus_transaction_t xbt, const char *path,
|
||||
XenbusState state)
|
||||
{
|
||||
char *current_state;
|
||||
char *msg = NULL;
|
||||
char *msg2 = NULL;
|
||||
char value[2];
|
||||
XenbusState rs;
|
||||
int xbt_flag = 0;
|
||||
int retry = 0;
|
||||
|
||||
do {
|
||||
if (xbt == XBT_NIL) {
|
||||
msg = xenbus_transaction_start(&xbt);
|
||||
if (msg)
|
||||
goto exit;
|
||||
xbt_flag = 1;
|
||||
}
|
||||
|
||||
msg = xenbus_read(xbt, path, ¤t_state);
|
||||
if (msg)
|
||||
goto exit;
|
||||
|
||||
rs = (XenbusState)(current_state[0] - '0');
|
||||
free(current_state);
|
||||
if (rs == state) {
|
||||
msg = NULL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
snprintf(value, 2, "%d", state);
|
||||
msg = xenbus_write(xbt, path, value);
|
||||
|
||||
exit:
|
||||
if (xbt_flag) {
|
||||
msg2 = xenbus_transaction_end(xbt, 0, &retry);
|
||||
xbt = XBT_NIL;
|
||||
}
|
||||
if (msg == NULL && msg2 != NULL)
|
||||
msg = msg2;
|
||||
else
|
||||
free(msg2);
|
||||
} while (retry);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
char *xenbus_wait_for_state_change(const char *path, XenbusState *state)
|
||||
{
|
||||
for (;;) {
|
||||
char *res, *msg;
|
||||
XenbusState rs;
|
||||
|
||||
msg = xenbus_read(XBT_NIL, path, &res);
|
||||
if (msg)
|
||||
return msg;
|
||||
|
||||
rs = (XenbusState)(res[0] - 48);
|
||||
free(res);
|
||||
|
||||
if (rs == *state) {
|
||||
wait_event_timeout(NULL, false, WAIT_XENBUS_POLL_TO_MS);
|
||||
} else {
|
||||
*state = rs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Send data to xenbus. This can block. All of the requests are seen
|
||||
* by xenbus as if sent atomically. The header is added
|
||||
* automatically, using type %type, req_id %req_id, and trans_id
|
||||
* %trans_id.
|
||||
*/
|
||||
static void xb_write(int type, int req_id, xenbus_transaction_t trans_id,
|
||||
const struct write_req *req, int nr_reqs)
|
||||
{
|
||||
XENSTORE_RING_IDX prod;
|
||||
int r;
|
||||
int len = 0;
|
||||
const struct write_req *cur_req;
|
||||
int req_off;
|
||||
int total_off;
|
||||
int this_chunk;
|
||||
struct xsd_sockmsg m = {
|
||||
.type = type,
|
||||
.req_id = req_id,
|
||||
.tx_id = trans_id
|
||||
};
|
||||
struct write_req header_req = {
|
||||
&m,
|
||||
sizeof(m)
|
||||
};
|
||||
|
||||
for (r = 0; r < nr_reqs; r++)
|
||||
len += req[r].len;
|
||||
m.len = len;
|
||||
len += sizeof(m);
|
||||
|
||||
cur_req = &header_req;
|
||||
|
||||
BUG_ON(len > XENSTORE_RING_SIZE);
|
||||
prod = xenstore_buf->req_prod;
|
||||
/* We are running synchronously, so it is a bug if we do not
|
||||
* have enough room to send a message: please note that a message
|
||||
* can occupy multiple slots in the ring buffer.
|
||||
*/
|
||||
BUG_ON(prod + len - xenstore_buf->req_cons > XENSTORE_RING_SIZE);
|
||||
|
||||
total_off = 0;
|
||||
req_off = 0;
|
||||
while (total_off < len) {
|
||||
this_chunk = min(cur_req->len - req_off,
|
||||
XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod));
|
||||
memcpy((char *)xenstore_buf->req + MASK_XENSTORE_IDX(prod),
|
||||
(char *)cur_req->data + req_off, this_chunk);
|
||||
prod += this_chunk;
|
||||
req_off += this_chunk;
|
||||
total_off += this_chunk;
|
||||
if (req_off == cur_req->len) {
|
||||
req_off = 0;
|
||||
if (cur_req == &header_req)
|
||||
cur_req = req;
|
||||
else
|
||||
cur_req++;
|
||||
}
|
||||
}
|
||||
|
||||
BUG_ON(req_off != 0);
|
||||
BUG_ON(total_off != len);
|
||||
BUG_ON(prod > xenstore_buf->req_cons + XENSTORE_RING_SIZE);
|
||||
|
||||
/* Remote must see entire message before updating indexes */
|
||||
wmb();
|
||||
|
||||
xenstore_buf->req_prod += len;
|
||||
|
||||
/* Send evtchn to notify remote */
|
||||
notify_remote_via_evtchn(xenbus_evtchn);
|
||||
}
|
||||
|
||||
/* Send a message to xenbus, in the same fashion as xb_write, and
|
||||
* block waiting for a reply. The reply is malloced and should be
|
||||
* freed by the caller.
|
||||
*/
|
||||
struct xsd_sockmsg *xenbus_msg_reply(int type,
|
||||
xenbus_transaction_t trans,
|
||||
struct write_req *io,
|
||||
int nr_reqs)
|
||||
{
|
||||
struct xsd_sockmsg *rep;
|
||||
|
||||
/* We do not use request identifier which is echoed in daemon's response. */
|
||||
xb_write(type, 0, trans, io, nr_reqs);
|
||||
/* Now wait for the message to arrive. */
|
||||
if (!xenbus_get_reply(&rep))
|
||||
return NULL;
|
||||
return rep;
|
||||
}
|
||||
|
||||
static char *errmsg(struct xsd_sockmsg *rep)
|
||||
{
|
||||
char *res;
|
||||
|
||||
if (!rep) {
|
||||
char msg[] = "No reply";
|
||||
size_t len = strlen(msg) + 1;
|
||||
|
||||
return memcpy(malloc(len), msg, len);
|
||||
}
|
||||
if (rep->type != XS_ERROR)
|
||||
return NULL;
|
||||
res = malloc(rep->len + 1);
|
||||
memcpy(res, rep + 1, rep->len);
|
||||
res[rep->len] = 0;
|
||||
free(rep);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* List the contents of a directory. Returns a malloc()ed array of
|
||||
* pointers to malloc()ed strings. The array is NULL terminated. May
|
||||
* block.
|
||||
*/
|
||||
char *xenbus_ls(xenbus_transaction_t xbt, const char *pre, char ***contents)
|
||||
{
|
||||
struct xsd_sockmsg *reply, *repmsg;
|
||||
struct write_req req[] = { { pre, strlen(pre) + 1 } };
|
||||
int nr_elems, x, i;
|
||||
char **res, *msg;
|
||||
|
||||
repmsg = xenbus_msg_reply(XS_DIRECTORY, xbt, req, ARRAY_SIZE(req));
|
||||
msg = errmsg(repmsg);
|
||||
if (msg) {
|
||||
*contents = NULL;
|
||||
return msg;
|
||||
}
|
||||
reply = repmsg + 1;
|
||||
for (x = nr_elems = 0; x < repmsg->len; x++)
|
||||
nr_elems += (((char *)reply)[x] == 0);
|
||||
res = malloc(sizeof(res[0]) * (nr_elems + 1));
|
||||
for (x = i = 0; i < nr_elems; i++) {
|
||||
int l = strlen((char *)reply + x);
|
||||
|
||||
res[i] = malloc(l + 1);
|
||||
memcpy(res[i], (char *)reply + x, l + 1);
|
||||
x += l + 1;
|
||||
}
|
||||
res[i] = NULL;
|
||||
free(repmsg);
|
||||
*contents = res;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *xenbus_read(xenbus_transaction_t xbt, const char *path, char **value)
|
||||
{
|
||||
struct write_req req[] = { {path, strlen(path) + 1} };
|
||||
struct xsd_sockmsg *rep;
|
||||
char *res, *msg;
|
||||
|
||||
rep = xenbus_msg_reply(XS_READ, xbt, req, ARRAY_SIZE(req));
|
||||
msg = errmsg(rep);
|
||||
if (msg) {
|
||||
*value = NULL;
|
||||
return msg;
|
||||
}
|
||||
res = malloc(rep->len + 1);
|
||||
memcpy(res, rep + 1, rep->len);
|
||||
res[rep->len] = 0;
|
||||
free(rep);
|
||||
*value = res;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *xenbus_write(xenbus_transaction_t xbt, const char *path,
|
||||
const char *value)
|
||||
{
|
||||
struct write_req req[] = {
|
||||
{path, strlen(path) + 1},
|
||||
{value, strlen(value)},
|
||||
};
|
||||
struct xsd_sockmsg *rep;
|
||||
char *msg;
|
||||
|
||||
rep = xenbus_msg_reply(XS_WRITE, xbt, req, ARRAY_SIZE(req));
|
||||
msg = errmsg(rep);
|
||||
if (msg)
|
||||
return msg;
|
||||
free(rep);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *xenbus_rm(xenbus_transaction_t xbt, const char *path)
|
||||
{
|
||||
struct write_req req[] = { {path, strlen(path) + 1} };
|
||||
struct xsd_sockmsg *rep;
|
||||
char *msg;
|
||||
|
||||
rep = xenbus_msg_reply(XS_RM, xbt, req, ARRAY_SIZE(req));
|
||||
msg = errmsg(rep);
|
||||
if (msg)
|
||||
return msg;
|
||||
free(rep);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *xenbus_get_perms(xenbus_transaction_t xbt, const char *path, char **value)
|
||||
{
|
||||
struct write_req req[] = { {path, strlen(path) + 1} };
|
||||
struct xsd_sockmsg *rep;
|
||||
char *res, *msg;
|
||||
|
||||
rep = xenbus_msg_reply(XS_GET_PERMS, xbt, req, ARRAY_SIZE(req));
|
||||
msg = errmsg(rep);
|
||||
if (msg) {
|
||||
*value = NULL;
|
||||
return msg;
|
||||
}
|
||||
res = malloc(rep->len + 1);
|
||||
memcpy(res, rep + 1, rep->len);
|
||||
res[rep->len] = 0;
|
||||
free(rep);
|
||||
*value = res;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define PERM_MAX_SIZE 32
|
||||
char *xenbus_set_perms(xenbus_transaction_t xbt, const char *path,
|
||||
domid_t dom, char perm)
|
||||
{
|
||||
char value[PERM_MAX_SIZE];
|
||||
struct write_req req[] = {
|
||||
{path, strlen(path) + 1},
|
||||
{value, 0},
|
||||
};
|
||||
struct xsd_sockmsg *rep;
|
||||
char *msg;
|
||||
|
||||
snprintf(value, PERM_MAX_SIZE, "%c%hu", perm, dom);
|
||||
req[1].len = strlen(value) + 1;
|
||||
rep = xenbus_msg_reply(XS_SET_PERMS, xbt, req, ARRAY_SIZE(req));
|
||||
msg = errmsg(rep);
|
||||
if (msg)
|
||||
return msg;
|
||||
free(rep);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *xenbus_transaction_start(xenbus_transaction_t *xbt)
|
||||
{
|
||||
/* Xenstored becomes angry if you send a length 0 message, so just
|
||||
* shove a nul terminator on the end
|
||||
*/
|
||||
struct write_req req = { "", 1};
|
||||
struct xsd_sockmsg *rep;
|
||||
char *err;
|
||||
|
||||
rep = xenbus_msg_reply(XS_TRANSACTION_START, 0, &req, 1);
|
||||
err = errmsg(rep);
|
||||
if (err)
|
||||
return err;
|
||||
sscanf((char *)(rep + 1), "%lu", xbt);
|
||||
free(rep);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *xenbus_transaction_end(xenbus_transaction_t t, int abort, int *retry)
|
||||
{
|
||||
struct xsd_sockmsg *rep;
|
||||
struct write_req req;
|
||||
char *err;
|
||||
|
||||
*retry = 0;
|
||||
|
||||
req.data = abort ? "F" : "T";
|
||||
req.len = 2;
|
||||
rep = xenbus_msg_reply(XS_TRANSACTION_END, t, &req, 1);
|
||||
err = errmsg(rep);
|
||||
if (err) {
|
||||
if (!strcmp(err, "EAGAIN")) {
|
||||
*retry = 1;
|
||||
free(err);
|
||||
return NULL;
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
free(rep);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int xenbus_read_integer(const char *path)
|
||||
{
|
||||
char *res, *buf;
|
||||
int t;
|
||||
|
||||
res = xenbus_read(XBT_NIL, path, &buf);
|
||||
if (res) {
|
||||
printk("Failed to read %s.\n", path);
|
||||
free(res);
|
||||
return -1;
|
||||
}
|
||||
sscanf(buf, "%d", &t);
|
||||
free(buf);
|
||||
return t;
|
||||
}
|
||||
|
||||
int xenbus_read_uuid(const char *path, unsigned char uuid[16])
|
||||
{
|
||||
char *res, *buf;
|
||||
|
||||
res = xenbus_read(XBT_NIL, path, &buf);
|
||||
if (res) {
|
||||
printk("Failed to read %s.\n", path);
|
||||
free(res);
|
||||
return 0;
|
||||
}
|
||||
if (strlen(buf) != ((2 * 16) + 4) /* 16 hex bytes and 4 hyphens */
|
||||
|| sscanf(buf,
|
||||
"%2hhx%2hhx%2hhx%2hhx-"
|
||||
"%2hhx%2hhx-"
|
||||
"%2hhx%2hhx-"
|
||||
"%2hhx%2hhx-"
|
||||
"%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
|
||||
uuid, uuid + 1, uuid + 2, uuid + 3,
|
||||
uuid + 4, uuid + 5, uuid + 6, uuid + 7,
|
||||
uuid + 8, uuid + 9, uuid + 10, uuid + 11,
|
||||
uuid + 12, uuid + 13, uuid + 14, uuid + 15) != 16) {
|
||||
printk("Xenbus path %s value %s is not a uuid!\n", path, buf);
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
free(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *xenbus_printf(xenbus_transaction_t xbt,
|
||||
const char *node, const char *path,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
#define BUFFER_SIZE 256
|
||||
char fullpath[BUFFER_SIZE];
|
||||
char val[BUFFER_SIZE];
|
||||
va_list args;
|
||||
|
||||
BUG_ON(strlen(node) + strlen(path) + 1 >= BUFFER_SIZE);
|
||||
sprintf(fullpath, "%s/%s", node, path);
|
||||
va_start(args, fmt);
|
||||
vsprintf(val, fmt, args);
|
||||
va_end(args);
|
||||
return xenbus_write(xbt, fullpath, val);
|
||||
}
|
||||
|
||||
domid_t xenbus_get_self_id(void)
|
||||
{
|
||||
char *dom_id;
|
||||
domid_t ret;
|
||||
|
||||
BUG_ON(xenbus_read(XBT_NIL, "domid", &dom_id));
|
||||
sscanf(dom_id, "%"SCNd16, &ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void init_xenbus(void)
|
||||
{
|
||||
u64 v;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
if (hvm_get_parameter(HVM_PARAM_STORE_EVTCHN, &v))
|
||||
BUG();
|
||||
xenbus_evtchn = v;
|
||||
|
||||
if (hvm_get_parameter(HVM_PARAM_STORE_PFN, &v))
|
||||
BUG();
|
||||
xenstore_buf = (struct xenstore_domain_interface *)map_frame_virt(v);
|
||||
}
|
||||
|
||||
void fini_xenbus(void)
|
||||
{
|
||||
debug("%s\n", __func__);
|
||||
}
|
@ -33,6 +33,7 @@ enum if_type {
|
||||
IF_TYPE_HOST,
|
||||
IF_TYPE_NVME,
|
||||
IF_TYPE_EFI,
|
||||
IF_TYPE_PVBLOCK,
|
||||
IF_TYPE_VIRTIO,
|
||||
|
||||
IF_TYPE_COUNT, /* Number of interface types */
|
||||
|
@ -7,22 +7,24 @@
|
||||
* (C) Copyright 2016 Google, Inc
|
||||
*/
|
||||
|
||||
#ifndef __AST_COMMON_CONFIG_H
|
||||
#define __AST_COMMON_CONFIG_H
|
||||
#ifndef _ASPEED_COMMON_CONFIG_H
|
||||
#define _ASPEED_COMMON_CONFIG_H
|
||||
|
||||
#include <asm/arch/platform.h>
|
||||
|
||||
/* Misc CPU related */
|
||||
#define CONFIG_CMDLINE_TAG
|
||||
#define CONFIG_SETUP_MEMORY_TAGS
|
||||
#define CONFIG_INITRD_TAG
|
||||
|
||||
#define CONFIG_SYS_SDRAM_BASE 0x80000000
|
||||
#define CONFIG_SYS_SDRAM_BASE ASPEED_DRAM_BASE
|
||||
|
||||
#ifdef CONFIG_PRE_CON_BUF_SZ
|
||||
#define CONFIG_SYS_INIT_RAM_ADDR (0x1e720000 + CONFIG_PRE_CON_BUF_SZ)
|
||||
#define CONFIG_SYS_INIT_RAM_SIZE (36*1024 - CONFIG_PRE_CON_BUF_SZ)
|
||||
#define CONFIG_SYS_INIT_RAM_ADDR (ASPEED_SRAM_BASE + CONFIG_PRE_CON_BUF_SZ)
|
||||
#define CONFIG_SYS_INIT_RAM_SIZE (ASPEED_SRAM_SIZE - CONFIG_PRE_CON_BUF_SZ)
|
||||
#else
|
||||
#define CONFIG_SYS_INIT_RAM_ADDR (0x1e720000)
|
||||
#define CONFIG_SYS_INIT_RAM_SIZE (36*1024)
|
||||
#define CONFIG_SYS_INIT_RAM_ADDR (ASPEED_SRAM_BASE)
|
||||
#define CONFIG_SYS_INIT_RAM_SIZE (ASPEED_SRAM_SIZE)
|
||||
#endif
|
||||
|
||||
#define SYS_INIT_RAM_END (CONFIG_SYS_INIT_RAM_ADDR \
|
||||
@ -45,8 +47,6 @@
|
||||
* Miscellaneous configurable options
|
||||
*/
|
||||
|
||||
#define CONFIG_BOOTCOMMAND "bootm 20080000 20300000"
|
||||
|
||||
#define CONFIG_EXTRA_ENV_SETTINGS \
|
||||
"verify=yes\0" \
|
||||
"spi_dma=yes\0" \
|
||||
|
52
include/configs/xenguest_arm64.h
Normal file
52
include/configs/xenguest_arm64.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* (C) Copyright 2020 EPAM Systemc Inc.
|
||||
*/
|
||||
#ifndef __XENGUEST_ARM64_H
|
||||
#define __XENGUEST_ARM64_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#include <linux/types.h>
|
||||
#endif
|
||||
|
||||
#define CONFIG_BOARD_EARLY_INIT_F
|
||||
|
||||
#define CONFIG_EXTRA_ENV_SETTINGS
|
||||
|
||||
#undef CONFIG_NR_DRAM_BANKS
|
||||
#undef CONFIG_SYS_SDRAM_BASE
|
||||
|
||||
#define CONFIG_NR_DRAM_BANKS 1
|
||||
|
||||
/*
|
||||
* This can be any arbitrary address as we are using PIE, but
|
||||
* please note, that CONFIG_SYS_TEXT_BASE must match the below.
|
||||
*/
|
||||
#define CONFIG_SYS_LOAD_ADDR 0x40000000
|
||||
#define CONFIG_LNX_KRNL_IMG_TEXT_OFFSET_BASE CONFIG_SYS_LOAD_ADDR
|
||||
|
||||
/* Size of malloc() pool */
|
||||
#define CONFIG_SYS_MALLOC_LEN (32 * 1024 * 1024)
|
||||
|
||||
/* Monitor Command Prompt */
|
||||
#define CONFIG_SYS_PROMPT_HUSH_PS2 "> "
|
||||
#define CONFIG_SYS_CBSIZE 1024
|
||||
#define CONFIG_SYS_MAXARGS 64
|
||||
#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE
|
||||
#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + \
|
||||
sizeof(CONFIG_SYS_PROMPT) + 16)
|
||||
|
||||
#define CONFIG_OF_SYSTEM_SETUP
|
||||
|
||||
#define CONFIG_CMDLINE_TAG 1
|
||||
#define CONFIG_INITRD_TAG 1
|
||||
|
||||
#define CONFIG_CMD_RUN
|
||||
|
||||
#undef CONFIG_EXTRA_ENV_SETTINGS
|
||||
#define CONFIG_EXTRA_ENV_SETTINGS \
|
||||
"loadimage=ext4load pvblock 0 0x90000000 /boot/Image;\0" \
|
||||
"pvblockboot=run loadimage;" \
|
||||
"booti 0x90000000 - 0x88000000;\0"
|
||||
|
||||
#endif /* __XENGUEST_ARM64_H */
|
@ -123,6 +123,7 @@ enum uclass_id {
|
||||
UCLASS_W1, /* Dallas 1-Wire bus */
|
||||
UCLASS_W1_EEPROM, /* one-wire EEPROMs */
|
||||
UCLASS_WDT, /* Watchdog Timer driver */
|
||||
UCLASS_PVBLOCK, /* Xen virtual block device */
|
||||
|
||||
UCLASS_COUNT,
|
||||
UCLASS_INVALID = -1,
|
||||
|
@ -1,12 +1,20 @@
|
||||
#ifndef _LINUX_COMPAT_H_
|
||||
#define _LINUX_COMPAT_H_
|
||||
|
||||
#include <console.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <asm/processor.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#ifdef CONFIG_XEN
|
||||
#include <xen/events.h>
|
||||
#endif
|
||||
|
||||
struct unused {};
|
||||
typedef struct unused unused_t;
|
||||
|
||||
@ -122,6 +130,52 @@ static inline void kmem_cache_destroy(struct kmem_cache *cachep)
|
||||
#define add_wait_queue(...) do { } while (0)
|
||||
#define remove_wait_queue(...) do { } while (0)
|
||||
|
||||
#ifndef CONFIG_XEN
|
||||
#define eventchn_poll()
|
||||
#endif
|
||||
|
||||
#define __wait_event_timeout(condition, timeout, ret) \
|
||||
({ \
|
||||
ulong __ret = ret; /* explicit shadow */ \
|
||||
ulong start = get_timer(0); \
|
||||
for (;;) { \
|
||||
eventchn_poll(); \
|
||||
if (condition) { \
|
||||
__ret = 1; \
|
||||
break; \
|
||||
} \
|
||||
if ((get_timer(start) > timeout) || ctrlc()) { \
|
||||
__ret = 0; \
|
||||
break; \
|
||||
} \
|
||||
cpu_relax(); \
|
||||
} \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
/**
|
||||
* wait_event_timeout() - Wait until the event occurs before the timeout.
|
||||
* @wr_head: The wait queue to wait on.
|
||||
* @condition: Expression for the event to wait for.
|
||||
* @timeout: Maximum waiting time.
|
||||
*
|
||||
* We wait until the @condition evaluates to %true (succeed) or
|
||||
* %false (@timeout elapsed).
|
||||
*
|
||||
* Return:
|
||||
* 0 - if the @condition evaluated to %false after the @timeout elapsed
|
||||
* 1 - if the @condition evaluated to %true
|
||||
*/
|
||||
#define wait_event_timeout(wq_head, condition, timeout) \
|
||||
({ \
|
||||
ulong __ret; \
|
||||
if (condition) \
|
||||
__ret = 1; \
|
||||
else \
|
||||
__ret = __wait_event_timeout(condition, timeout, __ret);\
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
|
||||
|
||||
/* This is also defined in ARMv8's mmu.h */
|
||||
|
@ -1315,7 +1315,8 @@ struct udevice *pci_get_controller(struct udevice *dev);
|
||||
*/
|
||||
int pci_get_regions(struct udevice *dev, struct pci_region **iop,
|
||||
struct pci_region **memp, struct pci_region **prefp);
|
||||
|
||||
int
|
||||
pci_get_dma_regions(struct udevice *dev, struct pci_region *memp, int index);
|
||||
/**
|
||||
* dm_pci_write_bar32() - Write the address of a BAR
|
||||
*
|
||||
|
17
include/pvblock.h
Normal file
17
include/pvblock.h
Normal file
@ -0,0 +1,17 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* (C) 2020 EPAM Systems Inc.
|
||||
*/
|
||||
|
||||
#ifndef _PVBLOCK_H
|
||||
#define _PVBLOCK_H
|
||||
|
||||
/**
|
||||
* pvblock_init() - Initialize para-virtual block device class driver
|
||||
*
|
||||
* Bind PV block to UCLASS_ROOT device and probe all UCLASS_PVBLOCK
|
||||
* virtual block devices.
|
||||
*/
|
||||
void pvblock_init(void);
|
||||
|
||||
#endif /* _PVBLOCK_H */
|
@ -234,4 +234,12 @@ char *strmhz(char *buf, unsigned long hz);
|
||||
*/
|
||||
void str_to_upper(const char *in, char *out, size_t len);
|
||||
|
||||
/**
|
||||
* sscanf - Unformat a buffer into a list of arguments
|
||||
* @buf: input buffer
|
||||
* @fmt: formatting of buffer
|
||||
* @...: resulting arguments
|
||||
*/
|
||||
int sscanf(const char *buf, const char *fmt, ...);
|
||||
|
||||
#endif
|
||||
|
24
include/xen.h
Normal file
24
include/xen.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* (C) 2020, EPAM Systems Inc.
|
||||
*/
|
||||
#ifndef __XEN_H__
|
||||
#define __XEN_H__
|
||||
|
||||
/**
|
||||
* xen_init() - Xen initialization
|
||||
*
|
||||
* Map Xen memory pages, initialize event handler and xenbus,
|
||||
* setup the grant table.
|
||||
*/
|
||||
void xen_init(void);
|
||||
|
||||
/**
|
||||
* xen_fini() - Board cleanup before Linux kernel start
|
||||
*
|
||||
* Unmap Xen memory pages the specified guest's pseudophysical
|
||||
* address space and unbind all event channels.
|
||||
*/
|
||||
void xen_fini(void);
|
||||
|
||||
#endif /* __XEN_H__ */
|
88
include/xen/arm/interface.h
Normal file
88
include/xen/arm/interface.h
Normal file
@ -0,0 +1,88 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Guest OS interface to ARM Xen.
|
||||
*
|
||||
* Stefano Stabellini <stefano.stabellini@eu.citrix.com>, Citrix, 2012
|
||||
*/
|
||||
|
||||
#ifndef _ASM_ARM_XEN_INTERFACE_H
|
||||
#define _ASM_ARM_XEN_INTERFACE_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#include <linux/types.h>
|
||||
#endif
|
||||
|
||||
#define uint64_aligned_t u64 __attribute__((aligned(8)))
|
||||
|
||||
#define __DEFINE_GUEST_HANDLE(name, type) \
|
||||
typedef struct { union { type * p; uint64_aligned_t q; }; } \
|
||||
__guest_handle_ ## name
|
||||
|
||||
#define DEFINE_GUEST_HANDLE_STRUCT(name) \
|
||||
__DEFINE_GUEST_HANDLE(name, struct name)
|
||||
#define DEFINE_GUEST_HANDLE(name) __DEFINE_GUEST_HANDLE(name, name)
|
||||
#define GUEST_HANDLE(name) __guest_handle_ ## name
|
||||
|
||||
#define set_xen_guest_handle(hnd, val) \
|
||||
do { \
|
||||
if (sizeof(hnd) == 8) \
|
||||
*(u64 *)&(hnd) = 0; \
|
||||
(hnd).p = val; \
|
||||
} while (0)
|
||||
|
||||
#define __HYPERVISOR_platform_op_raw __HYPERVISOR_platform_op
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
/* Explicitly size integers that represent pfns in the interface with
|
||||
* Xen so that we can have one ABI that works for 32 and 64 bit guests.
|
||||
* Note that this means that the xen_pfn_t type may be capable of
|
||||
* representing pfn's which the guest cannot represent in its own pfn
|
||||
* type. However since pfn space is controlled by the guest this is
|
||||
* fine since it simply wouldn't be able to create any sure pfns in
|
||||
* the first place.
|
||||
*/
|
||||
typedef u64 xen_pfn_t;
|
||||
#define PRI_xen_pfn "llx"
|
||||
typedef u64 xen_ulong_t;
|
||||
#define PRI_xen_ulong "llx"
|
||||
typedef s64 xen_long_t;
|
||||
#define PRI_xen_long "llx"
|
||||
/* Guest handles for primitive C types. */
|
||||
__DEFINE_GUEST_HANDLE(uchar, unsigned char);
|
||||
__DEFINE_GUEST_HANDLE(uint, unsigned int);
|
||||
DEFINE_GUEST_HANDLE(char);
|
||||
DEFINE_GUEST_HANDLE(int);
|
||||
DEFINE_GUEST_HANDLE(void);
|
||||
DEFINE_GUEST_HANDLE(u64);
|
||||
DEFINE_GUEST_HANDLE(u32);
|
||||
DEFINE_GUEST_HANDLE(xen_pfn_t);
|
||||
DEFINE_GUEST_HANDLE(xen_ulong_t);
|
||||
|
||||
/* Maximum number of virtual CPUs in multi-processor guests. */
|
||||
#define MAX_VIRT_CPUS 1
|
||||
|
||||
struct arch_vcpu_info { };
|
||||
struct arch_shared_info { };
|
||||
|
||||
/* TODO: Move pvclock definitions some place arch independent */
|
||||
struct pvclock_vcpu_time_info {
|
||||
u32 version;
|
||||
u32 pad0;
|
||||
u64 tsc_timestamp;
|
||||
u64 system_time;
|
||||
u32 tsc_to_system_mul;
|
||||
s8 tsc_shift;
|
||||
u8 flags;
|
||||
u8 pad[2];
|
||||
} __attribute__((__packed__)); /* 32 bytes */
|
||||
|
||||
/* It is OK to have a 12 bytes struct with no padding because it is packed */
|
||||
struct pvclock_wall_clock {
|
||||
u32 version;
|
||||
u32 sec;
|
||||
u32 nsec;
|
||||
u32 sec_hi;
|
||||
} __attribute__((__packed__));
|
||||
#endif
|
||||
|
||||
#endif /* _ASM_ARM_XEN_INTERFACE_H */
|
42
include/xen/events.h
Normal file
42
include/xen/events.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* (C) 2003 - Rolf Neugebauer - Intel Research Cambridge
|
||||
* (C) 2005 - Grzegorz Milos - Intel Reseach Cambridge
|
||||
* (C) 2020 - EPAM Systems Inc.
|
||||
*
|
||||
* File: events.h
|
||||
* Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
|
||||
* Changes: Grzegorz Milos (gm281@cam.ac.uk)
|
||||
*
|
||||
* Date: Jul 2003, changes Jun 2005
|
||||
*
|
||||
* Description: Deals with events on the event channels
|
||||
*/
|
||||
#ifndef _EVENTS_H_
|
||||
#define _EVENTS_H_
|
||||
|
||||
#include <asm/xen/hypercall.h>
|
||||
#include <xen/interface/event_channel.h>
|
||||
|
||||
void init_events(void);
|
||||
void fini_events(void);
|
||||
|
||||
int do_event(evtchn_port_t port, struct pt_regs *regs);
|
||||
void unbind_evtchn(evtchn_port_t port);
|
||||
void unbind_all_ports(void);
|
||||
int evtchn_alloc_unbound(domid_t pal,
|
||||
void (*handler)(evtchn_port_t, struct pt_regs *, void *),
|
||||
void *data, evtchn_port_t *port);
|
||||
|
||||
/* Send notification via event channel */
|
||||
static inline int notify_remote_via_evtchn(evtchn_port_t port)
|
||||
{
|
||||
struct evtchn_send op;
|
||||
|
||||
op.port = port;
|
||||
return HYPERVISOR_event_channel_op(EVTCHNOP_send, &op);
|
||||
}
|
||||
|
||||
void eventchn_poll(void);
|
||||
|
||||
#endif /* _EVENTS_H_ */
|
24
include/xen/gnttab.h
Normal file
24
include/xen/gnttab.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* (C) 2006, Steven Smith <sos22@cam.ac.uk>
|
||||
* (C) 2006, Grzegorz Milos <gm281@cam.ac.uk>
|
||||
* (C) 2020, EPAM Systems Inc.
|
||||
*/
|
||||
#ifndef __GNTTAB_H__
|
||||
#define __GNTTAB_H__
|
||||
|
||||
#include <xen/interface/grant_table.h>
|
||||
|
||||
void init_gnttab(void);
|
||||
void fini_gnttab(void);
|
||||
|
||||
grant_ref_t gnttab_alloc_and_grant(void **map);
|
||||
grant_ref_t gnttab_grant_access(domid_t domid, unsigned long frame,
|
||||
int readonly);
|
||||
int gnttab_end_access(grant_ref_t ref);
|
||||
const char *gnttabop_error(int16_t status);
|
||||
|
||||
void get_gnttab_base(phys_addr_t *gnttab_base, phys_size_t *gnttab_sz);
|
||||
|
||||
#endif /* !__GNTTAB_H__ */
|
27
include/xen/hvm.h
Normal file
27
include/xen/hvm.h
Normal file
@ -0,0 +1,27 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Simple wrappers around HVM functions
|
||||
*
|
||||
* Copyright (c) 2002-2003, K A Fraser
|
||||
* Copyright (c) 2005, Grzegorz Milos, gm281@cam.ac.uk,Intel Research Cambridge
|
||||
* Copyright (c) 2020, EPAM Systems Inc.
|
||||
*/
|
||||
#ifndef XEN_HVM_H__
|
||||
#define XEN_HVM_H__
|
||||
|
||||
#include <asm/xen/hypercall.h>
|
||||
#include <xen/interface/hvm/params.h>
|
||||
#include <xen/interface/xen.h>
|
||||
|
||||
extern struct shared_info *HYPERVISOR_shared_info;
|
||||
|
||||
int hvm_get_parameter(int idx, uint64_t *value);
|
||||
int hvm_get_parameter_maintain_dcache(int idx, uint64_t *value);
|
||||
|
||||
struct shared_info *map_shared_info(void *p);
|
||||
void do_hypervisor_callback(struct pt_regs *regs);
|
||||
void mask_evtchn(uint32_t port);
|
||||
void unmask_evtchn(uint32_t port);
|
||||
void clear_evtchn(uint32_t port);
|
||||
|
||||
#endif /* XEN_HVM_H__ */
|
279
include/xen/interface/event_channel.h
Normal file
279
include/xen/interface/event_channel.h
Normal file
@ -0,0 +1,279 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* event_channel.h
|
||||
*
|
||||
* Event channels between domains.
|
||||
*
|
||||
* Copyright (c) 2003-2004, K A Fraser.
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PUBLIC_EVENT_CHANNEL_H__
|
||||
#define __XEN_PUBLIC_EVENT_CHANNEL_H__
|
||||
|
||||
#include <xen/interface/xen.h>
|
||||
|
||||
typedef u32 evtchn_port_t;
|
||||
DEFINE_GUEST_HANDLE(evtchn_port_t);
|
||||
|
||||
/*
|
||||
* EVTCHNOP_alloc_unbound: Allocate a port in domain <dom> and mark as
|
||||
* accepting interdomain bindings from domain <remote_dom>. A fresh port
|
||||
* is allocated in <dom> and returned as <port>.
|
||||
* NOTES:
|
||||
* 1. If the caller is unprivileged then <dom> must be DOMID_SELF.
|
||||
* 2. <rdom> may be DOMID_SELF, allowing loopback connections.
|
||||
*/
|
||||
#define EVTCHNOP_alloc_unbound 6
|
||||
struct evtchn_alloc_unbound {
|
||||
/* IN parameters */
|
||||
domid_t dom, remote_dom;
|
||||
/* OUT parameters */
|
||||
evtchn_port_t port;
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_bind_interdomain: Construct an interdomain event channel between
|
||||
* the calling domain and <remote_dom>. <remote_dom,remote_port> must identify
|
||||
* a port that is unbound and marked as accepting bindings from the calling
|
||||
* domain. A fresh port is allocated in the calling domain and returned as
|
||||
* <local_port>.
|
||||
* NOTES:
|
||||
* 2. <remote_dom> may be DOMID_SELF, allowing loopback connections.
|
||||
*/
|
||||
#define EVTCHNOP_bind_interdomain 0
|
||||
struct evtchn_bind_interdomain {
|
||||
/* IN parameters. */
|
||||
domid_t remote_dom;
|
||||
evtchn_port_t remote_port;
|
||||
/* OUT parameters. */
|
||||
evtchn_port_t local_port;
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_bind_virq: Bind a local event channel to VIRQ <irq> on specified
|
||||
* vcpu.
|
||||
* NOTES:
|
||||
* 1. A virtual IRQ may be bound to at most one event channel per vcpu.
|
||||
* 2. The allocated event channel is bound to the specified vcpu. The binding
|
||||
* may not be changed.
|
||||
*/
|
||||
#define EVTCHNOP_bind_virq 1
|
||||
struct evtchn_bind_virq {
|
||||
/* IN parameters. */
|
||||
u32 virq;
|
||||
u32 vcpu;
|
||||
/* OUT parameters. */
|
||||
evtchn_port_t port;
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_bind_pirq: Bind a local event channel to PIRQ <irq>.
|
||||
* NOTES:
|
||||
* 1. A physical IRQ may be bound to at most one event channel per domain.
|
||||
* 2. Only a sufficiently-privileged domain may bind to a physical IRQ.
|
||||
*/
|
||||
#define EVTCHNOP_bind_pirq 2
|
||||
struct evtchn_bind_pirq {
|
||||
/* IN parameters. */
|
||||
u32 pirq;
|
||||
#define BIND_PIRQ__WILL_SHARE 1
|
||||
u32 flags; /* BIND_PIRQ__* */
|
||||
/* OUT parameters. */
|
||||
evtchn_port_t port;
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_bind_ipi: Bind a local event channel to receive events.
|
||||
* NOTES:
|
||||
* 1. The allocated event channel is bound to the specified vcpu. The binding
|
||||
* may not be changed.
|
||||
*/
|
||||
#define EVTCHNOP_bind_ipi 7
|
||||
struct evtchn_bind_ipi {
|
||||
u32 vcpu;
|
||||
/* OUT parameters. */
|
||||
evtchn_port_t port;
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_close: Close a local event channel <port>. If the channel is
|
||||
* interdomain then the remote end is placed in the unbound state
|
||||
* (EVTCHNSTAT_unbound), awaiting a new connection.
|
||||
*/
|
||||
#define EVTCHNOP_close 3
|
||||
struct evtchn_close {
|
||||
/* IN parameters. */
|
||||
evtchn_port_t port;
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_send: Send an event to the remote end of the channel whose local
|
||||
* endpoint is <port>.
|
||||
*/
|
||||
#define EVTCHNOP_send 4
|
||||
struct evtchn_send {
|
||||
/* IN parameters. */
|
||||
evtchn_port_t port;
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_status: Get the current status of the communication channel which
|
||||
* has an endpoint at <dom, port>.
|
||||
* NOTES:
|
||||
* 1. <dom> may be specified as DOMID_SELF.
|
||||
* 2. Only a sufficiently-privileged domain may obtain the status of an event
|
||||
* channel for which <dom> is not DOMID_SELF.
|
||||
*/
|
||||
#define EVTCHNOP_status 5
|
||||
struct evtchn_status {
|
||||
/* IN parameters */
|
||||
domid_t dom;
|
||||
evtchn_port_t port;
|
||||
/* OUT parameters */
|
||||
#define EVTCHNSTAT_closed 0 /* Channel is not in use. */
|
||||
#define EVTCHNSTAT_unbound 1 /* Channel is waiting interdom connection.*/
|
||||
#define EVTCHNSTAT_interdomain 2 /* Channel is connected to remote domain. */
|
||||
#define EVTCHNSTAT_pirq 3 /* Channel is bound to a phys IRQ line. */
|
||||
#define EVTCHNSTAT_virq 4 /* Channel is bound to a virtual IRQ line */
|
||||
#define EVTCHNSTAT_ipi 5 /* Channel is bound to a virtual IPI line */
|
||||
u32 status;
|
||||
u32 vcpu; /* VCPU to which this channel is bound. */
|
||||
union {
|
||||
struct {
|
||||
domid_t dom;
|
||||
} unbound; /* EVTCHNSTAT_unbound */
|
||||
struct {
|
||||
domid_t dom;
|
||||
evtchn_port_t port;
|
||||
} interdomain; /* EVTCHNSTAT_interdomain */
|
||||
u32 pirq; /* EVTCHNSTAT_pirq */
|
||||
u32 virq; /* EVTCHNSTAT_virq */
|
||||
} u;
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_bind_vcpu: Specify which vcpu a channel should notify when an
|
||||
* event is pending.
|
||||
* NOTES:
|
||||
* 1. IPI- and VIRQ-bound channels always notify the vcpu that initialised
|
||||
* the binding. This binding cannot be changed.
|
||||
* 2. All other channels notify vcpu0 by default. This default is set when
|
||||
* the channel is allocated (a port that is freed and subsequently reused
|
||||
* has its binding reset to vcpu0).
|
||||
*/
|
||||
#define EVTCHNOP_bind_vcpu 8
|
||||
struct evtchn_bind_vcpu {
|
||||
/* IN parameters. */
|
||||
evtchn_port_t port;
|
||||
u32 vcpu;
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_unmask: Unmask the specified local event-channel port and deliver
|
||||
* a notification to the appropriate VCPU if an event is pending.
|
||||
*/
|
||||
#define EVTCHNOP_unmask 9
|
||||
struct evtchn_unmask {
|
||||
/* IN parameters. */
|
||||
evtchn_port_t port;
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_reset: Close all event channels associated with specified domain.
|
||||
* NOTES:
|
||||
* 1. <dom> may be specified as DOMID_SELF.
|
||||
* 2. Only a sufficiently-privileged domain may specify other than DOMID_SELF.
|
||||
*/
|
||||
#define EVTCHNOP_reset 10
|
||||
struct evtchn_reset {
|
||||
/* IN parameters. */
|
||||
domid_t dom;
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_init_control: initialize the control block for the FIFO ABI.
|
||||
*/
|
||||
#define EVTCHNOP_init_control 11
|
||||
struct evtchn_init_control {
|
||||
/* IN parameters. */
|
||||
u64 control_gfn;
|
||||
u32 offset;
|
||||
u32 vcpu;
|
||||
/* OUT parameters. */
|
||||
u8 link_bits;
|
||||
u8 _pad[7];
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_expand_array: add an additional page to the event array.
|
||||
*/
|
||||
#define EVTCHNOP_expand_array 12
|
||||
struct evtchn_expand_array {
|
||||
/* IN parameters. */
|
||||
u64 array_gfn;
|
||||
};
|
||||
|
||||
/*
|
||||
* EVTCHNOP_set_priority: set the priority for an event channel.
|
||||
*/
|
||||
#define EVTCHNOP_set_priority 13
|
||||
struct evtchn_set_priority {
|
||||
/* IN parameters. */
|
||||
evtchn_port_t port;
|
||||
u32 priority;
|
||||
};
|
||||
|
||||
struct evtchn_op {
|
||||
u32 cmd; /* EVTCHNOP_* */
|
||||
union {
|
||||
struct evtchn_alloc_unbound alloc_unbound;
|
||||
struct evtchn_bind_interdomain bind_interdomain;
|
||||
struct evtchn_bind_virq bind_virq;
|
||||
struct evtchn_bind_pirq bind_pirq;
|
||||
struct evtchn_bind_ipi bind_ipi;
|
||||
struct evtchn_close close;
|
||||
struct evtchn_send send;
|
||||
struct evtchn_status status;
|
||||
struct evtchn_bind_vcpu bind_vcpu;
|
||||
struct evtchn_unmask unmask;
|
||||
} u;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(evtchn_op);
|
||||
|
||||
/*
|
||||
* 2-level ABI
|
||||
*/
|
||||
|
||||
#define EVTCHN_2L_NR_CHANNELS (sizeof(xen_ulong_t) * sizeof(xen_ulong_t) * 64)
|
||||
|
||||
/*
|
||||
* FIFO ABI
|
||||
*/
|
||||
|
||||
/* Events may have priorities from 0 (highest) to 15 (lowest). */
|
||||
#define EVTCHN_FIFO_PRIORITY_MAX 0
|
||||
#define EVTCHN_FIFO_PRIORITY_DEFAULT 7
|
||||
#define EVTCHN_FIFO_PRIORITY_MIN 15
|
||||
|
||||
#define EVTCHN_FIFO_MAX_QUEUES (EVTCHN_FIFO_PRIORITY_MIN + 1)
|
||||
|
||||
typedef u32 event_word_t;
|
||||
|
||||
#define EVTCHN_FIFO_PENDING 31
|
||||
#define EVTCHN_FIFO_MASKED 30
|
||||
#define EVTCHN_FIFO_LINKED 29
|
||||
#define EVTCHN_FIFO_BUSY 28
|
||||
|
||||
#define EVTCHN_FIFO_LINK_BITS 17
|
||||
#define EVTCHN_FIFO_LINK_MASK ((1 << EVTCHN_FIFO_LINK_BITS) - 1)
|
||||
|
||||
#define EVTCHN_FIFO_NR_CHANNELS (1 << EVTCHN_FIFO_LINK_BITS)
|
||||
|
||||
struct evtchn_fifo_control_block {
|
||||
u32 ready;
|
||||
u32 _rsvd;
|
||||
event_word_t head[EVTCHN_FIFO_MAX_QUEUES];
|
||||
};
|
||||
|
||||
#endif /* __XEN_PUBLIC_EVENT_CHANNEL_H__ */
|
565
include/xen/interface/grant_table.h
Normal file
565
include/xen/interface/grant_table.h
Normal file
@ -0,0 +1,565 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* grant_table.h
|
||||
*
|
||||
* Interface for granting foreign access to page frames, and receiving
|
||||
* page-ownership transfers.
|
||||
*
|
||||
* Copyright (c) 2004, K A Fraser
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PUBLIC_GRANT_TABLE_H__
|
||||
#define __XEN_PUBLIC_GRANT_TABLE_H__
|
||||
|
||||
#include <xen/interface/xen.h>
|
||||
|
||||
/***********************************
|
||||
* GRANT TABLE REPRESENTATION
|
||||
*/
|
||||
|
||||
/* Some rough guidelines on accessing and updating grant-table entries
|
||||
* in a concurrency-safe manner. For more information, Linux contains a
|
||||
* reference implementation for guest OSes (arch/xen/kernel/grant_table.c).
|
||||
*
|
||||
* NB. WMB is a no-op on current-generation x86 processors. However, a
|
||||
* compiler barrier will still be required.
|
||||
*
|
||||
* Introducing a valid entry into the grant table:
|
||||
* 1. Write ent->domid.
|
||||
* 2. Write ent->frame:
|
||||
* GTF_permit_access: Frame to which access is permitted.
|
||||
* GTF_accept_transfer: Pseudo-phys frame slot being filled by new
|
||||
* frame, or zero if none.
|
||||
* 3. Write memory barrier (WMB).
|
||||
* 4. Write ent->flags, inc. valid type.
|
||||
*
|
||||
* Invalidating an unused GTF_permit_access entry:
|
||||
* 1. flags = ent->flags.
|
||||
* 2. Observe that !(flags & (GTF_reading|GTF_writing)).
|
||||
* 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0).
|
||||
* NB. No need for WMB as reuse of entry is control-dependent on success of
|
||||
* step 3, and all architectures guarantee ordering of ctrl-dep writes.
|
||||
*
|
||||
* Invalidating an in-use GTF_permit_access entry:
|
||||
* This cannot be done directly. Request assistance from the domain controller
|
||||
* which can set a timeout on the use of a grant entry and take necessary
|
||||
* action. (NB. This is not yet implemented!).
|
||||
*
|
||||
* Invalidating an unused GTF_accept_transfer entry:
|
||||
* 1. flags = ent->flags.
|
||||
* 2. Observe that !(flags & GTF_transfer_committed). [*]
|
||||
* 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0).
|
||||
* NB. No need for WMB as reuse of entry is control-dependent on success of
|
||||
* step 3, and all architectures guarantee ordering of ctrl-dep writes.
|
||||
* [*] If GTF_transfer_committed is set then the grant entry is 'committed'.
|
||||
* The guest must /not/ modify the grant entry until the address of the
|
||||
* transferred frame is written. It is safe for the guest to spin waiting
|
||||
* for this to occur (detect by observing GTF_transfer_completed in
|
||||
* ent->flags).
|
||||
*
|
||||
* Invalidating a committed GTF_accept_transfer entry:
|
||||
* 1. Wait for (ent->flags & GTF_transfer_completed).
|
||||
*
|
||||
* Changing a GTF_permit_access from writable to read-only:
|
||||
* Use SMP-safe CMPXCHG to set GTF_readonly, while checking !GTF_writing.
|
||||
*
|
||||
* Changing a GTF_permit_access from read-only to writable:
|
||||
* Use SMP-safe bit-setting instruction.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Reference to a grant entry in a specified domain's grant table.
|
||||
*/
|
||||
typedef u32 grant_ref_t;
|
||||
|
||||
/*
|
||||
* A grant table comprises a packed array of grant entries in one or more
|
||||
* page frames shared between Xen and a guest.
|
||||
* [XEN]: This field is written by Xen and read by the sharing guest.
|
||||
* [GST]: This field is written by the guest and read by Xen.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Version 1 of the grant table entry structure is maintained purely
|
||||
* for backwards compatibility. New guests should use version 2.
|
||||
*/
|
||||
struct grant_entry_v1 {
|
||||
/* GTF_xxx: various type and flag information. [XEN,GST] */
|
||||
u16 flags;
|
||||
/* The domain being granted foreign privileges. [GST] */
|
||||
domid_t domid;
|
||||
/*
|
||||
* GTF_permit_access: Frame that @domid is allowed to map and access. [GST]
|
||||
* GTF_accept_transfer: Frame whose ownership transferred by @domid. [XEN]
|
||||
*/
|
||||
u32 frame;
|
||||
};
|
||||
|
||||
/*
|
||||
* Type of grant entry.
|
||||
* GTF_invalid: This grant entry grants no privileges.
|
||||
* GTF_permit_access: Allow @domid to map/access @frame.
|
||||
* GTF_accept_transfer: Allow @domid to transfer ownership of one page frame
|
||||
* to this guest. Xen writes the page number to @frame.
|
||||
* GTF_transitive: Allow @domid to transitively access a subrange of
|
||||
* @trans_grant in @trans_domid. No mappings are allowed.
|
||||
*/
|
||||
#define GTF_invalid (0U << 0)
|
||||
#define GTF_permit_access (1U << 0)
|
||||
#define GTF_accept_transfer (2U << 0)
|
||||
#define GTF_transitive (3U << 0)
|
||||
#define GTF_type_mask (3U << 0)
|
||||
|
||||
/*
|
||||
* Subflags for GTF_permit_access.
|
||||
* GTF_readonly: Restrict @domid to read-only mappings and accesses. [GST]
|
||||
* GTF_reading: Grant entry is currently mapped for reading by @domid. [XEN]
|
||||
* GTF_writing: Grant entry is currently mapped for writing by @domid. [XEN]
|
||||
* GTF_sub_page: Grant access to only a subrange of the page. @domid
|
||||
* will only be allowed to copy from the grant, and not
|
||||
* map it. [GST]
|
||||
*/
|
||||
#define _GTF_readonly (2)
|
||||
#define GTF_readonly (1U << _GTF_readonly)
|
||||
#define _GTF_reading (3)
|
||||
#define GTF_reading (1U << _GTF_reading)
|
||||
#define _GTF_writing (4)
|
||||
#define GTF_writing (1U << _GTF_writing)
|
||||
#define _GTF_sub_page (8)
|
||||
#define GTF_sub_page (1U << _GTF_sub_page)
|
||||
|
||||
/*
|
||||
* Subflags for GTF_accept_transfer:
|
||||
* GTF_transfer_committed: Xen sets this flag to indicate that it is committed
|
||||
* to transferring ownership of a page frame. When a guest sees this flag
|
||||
* it must /not/ modify the grant entry until GTF_transfer_completed is
|
||||
* set by Xen.
|
||||
* GTF_transfer_completed: It is safe for the guest to spin-wait on this flag
|
||||
* after reading GTF_transfer_committed. Xen will always write the frame
|
||||
* address, followed by ORing this flag, in a timely manner.
|
||||
*/
|
||||
#define _GTF_transfer_committed (2)
|
||||
#define GTF_transfer_committed (1U << _GTF_transfer_committed)
|
||||
#define _GTF_transfer_completed (3)
|
||||
#define GTF_transfer_completed (1U << _GTF_transfer_completed)
|
||||
|
||||
/*
|
||||
* Version 2 grant table entries. These fulfil the same role as
|
||||
* version 1 entries, but can represent more complicated operations.
|
||||
* Any given domain will have either a version 1 or a version 2 table,
|
||||
* and every entry in the table will be the same version.
|
||||
*
|
||||
* The interface by which domains use grant references does not depend
|
||||
* on the grant table version in use by the other domain.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Version 1 and version 2 grant entries share a common prefix. The
|
||||
* fields of the prefix are documented as part of struct
|
||||
* grant_entry_v1.
|
||||
*/
|
||||
struct grant_entry_header {
|
||||
u16 flags;
|
||||
domid_t domid;
|
||||
};
|
||||
|
||||
/*
|
||||
* Version 2 of the grant entry structure, here is a union because three
|
||||
* different types are suppotted: full_page, sub_page and transitive.
|
||||
*/
|
||||
union grant_entry_v2 {
|
||||
struct grant_entry_header hdr;
|
||||
|
||||
/*
|
||||
* This member is used for V1-style full page grants, where either:
|
||||
*
|
||||
* -- hdr.type is GTF_accept_transfer, or
|
||||
* -- hdr.type is GTF_permit_access and GTF_sub_page is not set.
|
||||
*
|
||||
* In that case, the frame field has the same semantics as the
|
||||
* field of the same name in the V1 entry structure.
|
||||
*/
|
||||
struct {
|
||||
struct grant_entry_header hdr;
|
||||
u32 pad0;
|
||||
u64 frame;
|
||||
} full_page;
|
||||
|
||||
/*
|
||||
* If the grant type is GTF_grant_access and GTF_sub_page is set,
|
||||
* @domid is allowed to access bytes [@page_off,@page_off+@length)
|
||||
* in frame @frame.
|
||||
*/
|
||||
struct {
|
||||
struct grant_entry_header hdr;
|
||||
u16 page_off;
|
||||
u16 length;
|
||||
u64 frame;
|
||||
} sub_page;
|
||||
|
||||
/*
|
||||
* If the grant is GTF_transitive, @domid is allowed to use the
|
||||
* grant @gref in domain @trans_domid, as if it was the local
|
||||
* domain. Obviously, the transitive access must be compatible
|
||||
* with the original grant.
|
||||
*/
|
||||
struct {
|
||||
struct grant_entry_header hdr;
|
||||
domid_t trans_domid;
|
||||
u16 pad0;
|
||||
grant_ref_t gref;
|
||||
} transitive;
|
||||
|
||||
u32 __spacer[4]; /* Pad to a power of two */
|
||||
};
|
||||
|
||||
typedef u16 grant_status_t;
|
||||
|
||||
/***********************************
|
||||
* GRANT TABLE QUERIES AND USES
|
||||
*/
|
||||
|
||||
/*
|
||||
* Handle to track a mapping created via a grant reference.
|
||||
*/
|
||||
typedef u32 grant_handle_t;
|
||||
|
||||
/*
|
||||
* GNTTABOP_map_grant_ref: Map the grant entry (<dom>,<ref>) for access
|
||||
* by devices and/or host CPUs. If successful, <handle> is a tracking number
|
||||
* that must be presented later to destroy the mapping(s). On error, <handle>
|
||||
* is a negative status code.
|
||||
* NOTES:
|
||||
* 1. If GNTMAP_device_map is specified then <dev_bus_addr> is the address
|
||||
* via which I/O devices may access the granted frame.
|
||||
* 2. If GNTMAP_host_map is specified then a mapping will be added at
|
||||
* either a host virtual address in the current address space, or at
|
||||
* a PTE at the specified machine address. The type of mapping to
|
||||
* perform is selected through the GNTMAP_contains_pte flag, and the
|
||||
* address is specified in <host_addr>.
|
||||
* 3. Mappings should only be destroyed via GNTTABOP_unmap_grant_ref. If a
|
||||
* host mapping is destroyed by other means then it is *NOT* guaranteed
|
||||
* to be accounted to the correct grant reference!
|
||||
*/
|
||||
#define GNTTABOP_map_grant_ref 0
|
||||
struct gnttab_map_grant_ref {
|
||||
/* IN parameters. */
|
||||
u64 host_addr;
|
||||
u32 flags; /* GNTMAP_* */
|
||||
grant_ref_t ref;
|
||||
domid_t dom;
|
||||
/* OUT parameters. */
|
||||
s16 status; /* GNTST_* */
|
||||
grant_handle_t handle;
|
||||
u64 dev_bus_addr;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(gnttab_map_grant_ref);
|
||||
|
||||
/*
|
||||
* GNTTABOP_unmap_grant_ref: Destroy one or more grant-reference mappings
|
||||
* tracked by <handle>. If <host_addr> or <dev_bus_addr> is zero, that
|
||||
* field is ignored. If non-zero, they must refer to a device/host mapping
|
||||
* that is tracked by <handle>
|
||||
* NOTES:
|
||||
* 1. The call may fail in an undefined manner if either mapping is not
|
||||
* tracked by <handle>.
|
||||
* 3. After executing a batch of unmaps, it is guaranteed that no stale
|
||||
* mappings will remain in the device or host TLBs.
|
||||
*/
|
||||
#define GNTTABOP_unmap_grant_ref 1
|
||||
struct gnttab_unmap_grant_ref {
|
||||
/* IN parameters. */
|
||||
u64 host_addr;
|
||||
u64 dev_bus_addr;
|
||||
grant_handle_t handle;
|
||||
/* OUT parameters. */
|
||||
s16 status; /* GNTST_* */
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(gnttab_unmap_grant_ref);
|
||||
|
||||
/*
|
||||
* GNTTABOP_setup_table: Set up a grant table for <dom> comprising at least
|
||||
* <nr_frames> pages. The frame addresses are written to the <frame_list>.
|
||||
* Only <nr_frames> addresses are written, even if the table is larger.
|
||||
* NOTES:
|
||||
* 1. <dom> may be specified as DOMID_SELF.
|
||||
* 2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF.
|
||||
* 3. Xen may not support more than a single grant-table page per domain.
|
||||
*/
|
||||
#define GNTTABOP_setup_table 2
|
||||
struct gnttab_setup_table {
|
||||
/* IN parameters. */
|
||||
domid_t dom;
|
||||
u32 nr_frames;
|
||||
/* OUT parameters. */
|
||||
s16 status; /* GNTST_* */
|
||||
|
||||
GUEST_HANDLE(xen_pfn_t)frame_list;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(gnttab_setup_table);
|
||||
|
||||
/*
|
||||
* GNTTABOP_dump_table: Dump the contents of the grant table to the
|
||||
* xen console. Debugging use only.
|
||||
*/
|
||||
#define GNTTABOP_dump_table 3
|
||||
struct gnttab_dump_table {
|
||||
/* IN parameters. */
|
||||
domid_t dom;
|
||||
/* OUT parameters. */
|
||||
s16 status; /* GNTST_* */
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(gnttab_dump_table);
|
||||
|
||||
/*
|
||||
* GNTTABOP_transfer_grant_ref: Transfer <frame> to a foreign domain. The
|
||||
* foreign domain has previously registered its interest in the transfer via
|
||||
* <domid, ref>.
|
||||
*
|
||||
* Note that, even if the transfer fails, the specified page no longer belongs
|
||||
* to the calling domain *unless* the error is GNTST_bad_page.
|
||||
*/
|
||||
#define GNTTABOP_transfer 4
|
||||
struct gnttab_transfer {
|
||||
/* IN parameters. */
|
||||
xen_pfn_t mfn;
|
||||
domid_t domid;
|
||||
grant_ref_t ref;
|
||||
/* OUT parameters. */
|
||||
s16 status;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(gnttab_transfer);
|
||||
|
||||
/*
|
||||
* GNTTABOP_copy: Hypervisor based copy
|
||||
* source and destinations can be eithers MFNs or, for foreign domains,
|
||||
* grant references. the foreign domain has to grant read/write access
|
||||
* in its grant table.
|
||||
*
|
||||
* The flags specify what type source and destinations are (either MFN
|
||||
* or grant reference).
|
||||
*
|
||||
* Note that this can also be used to copy data between two domains
|
||||
* via a third party if the source and destination domains had previously
|
||||
* grant appropriate access to their pages to the third party.
|
||||
*
|
||||
* source_offset specifies an offset in the source frame, dest_offset
|
||||
* the offset in the target frame and len specifies the number of
|
||||
* bytes to be copied.
|
||||
*/
|
||||
|
||||
#define _GNTCOPY_source_gref (0)
|
||||
#define GNTCOPY_source_gref (1 << _GNTCOPY_source_gref)
|
||||
#define _GNTCOPY_dest_gref (1)
|
||||
#define GNTCOPY_dest_gref (1 << _GNTCOPY_dest_gref)
|
||||
|
||||
#define GNTTABOP_copy 5
|
||||
struct gnttab_copy {
|
||||
/* IN parameters. */
|
||||
struct {
|
||||
union {
|
||||
grant_ref_t ref;
|
||||
xen_pfn_t gmfn;
|
||||
} u;
|
||||
domid_t domid;
|
||||
u16 offset;
|
||||
} source, dest;
|
||||
u16 len;
|
||||
u16 flags; /* GNTCOPY_* */
|
||||
/* OUT parameters. */
|
||||
s16 status;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(gnttab_copy);
|
||||
|
||||
/*
|
||||
* GNTTABOP_query_size: Query the current and maximum sizes of the shared
|
||||
* grant table.
|
||||
* NOTES:
|
||||
* 1. <dom> may be specified as DOMID_SELF.
|
||||
* 2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF.
|
||||
*/
|
||||
#define GNTTABOP_query_size 6
|
||||
struct gnttab_query_size {
|
||||
/* IN parameters. */
|
||||
domid_t dom;
|
||||
/* OUT parameters. */
|
||||
u32 nr_frames;
|
||||
u32 max_nr_frames;
|
||||
s16 status; /* GNTST_* */
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(gnttab_query_size);
|
||||
|
||||
/*
|
||||
* GNTTABOP_unmap_and_replace: Destroy one or more grant-reference mappings
|
||||
* tracked by <handle> but atomically replace the page table entry with one
|
||||
* pointing to the machine address under <new_addr>. <new_addr> will be
|
||||
* redirected to the null entry.
|
||||
* NOTES:
|
||||
* 1. The call may fail in an undefined manner if either mapping is not
|
||||
* tracked by <handle>.
|
||||
* 2. After executing a batch of unmaps, it is guaranteed that no stale
|
||||
* mappings will remain in the device or host TLBs.
|
||||
*/
|
||||
#define GNTTABOP_unmap_and_replace 7
|
||||
struct gnttab_unmap_and_replace {
|
||||
/* IN parameters. */
|
||||
u64 host_addr;
|
||||
u64 new_addr;
|
||||
grant_handle_t handle;
|
||||
/* OUT parameters. */
|
||||
s16 status; /* GNTST_* */
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(gnttab_unmap_and_replace);
|
||||
|
||||
/*
|
||||
* GNTTABOP_set_version: Request a particular version of the grant
|
||||
* table shared table structure. This operation can only be performed
|
||||
* once in any given domain. It must be performed before any grants
|
||||
* are activated; otherwise, the domain will be stuck with version 1.
|
||||
* The only defined versions are 1 and 2.
|
||||
*/
|
||||
#define GNTTABOP_set_version 8
|
||||
struct gnttab_set_version {
|
||||
/* IN parameters */
|
||||
u32 version;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(gnttab_set_version);
|
||||
|
||||
/*
|
||||
* GNTTABOP_get_status_frames: Get the list of frames used to store grant
|
||||
* status for <dom>. In grant format version 2, the status is separated
|
||||
* from the other shared grant fields to allow more efficient synchronization
|
||||
* using barriers instead of atomic cmpexch operations.
|
||||
* <nr_frames> specify the size of vector <frame_list>.
|
||||
* The frame addresses are returned in the <frame_list>.
|
||||
* Only <nr_frames> addresses are returned, even if the table is larger.
|
||||
* NOTES:
|
||||
* 1. <dom> may be specified as DOMID_SELF.
|
||||
* 2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF.
|
||||
*/
|
||||
#define GNTTABOP_get_status_frames 9
|
||||
struct gnttab_get_status_frames {
|
||||
/* IN parameters. */
|
||||
u32 nr_frames;
|
||||
domid_t dom;
|
||||
/* OUT parameters. */
|
||||
s16 status; /* GNTST_* */
|
||||
|
||||
GUEST_HANDLE(u64)frame_list;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(gnttab_get_status_frames);
|
||||
|
||||
/*
|
||||
* GNTTABOP_get_version: Get the grant table version which is in
|
||||
* effect for domain <dom>.
|
||||
*/
|
||||
#define GNTTABOP_get_version 10
|
||||
struct gnttab_get_version {
|
||||
/* IN parameters */
|
||||
domid_t dom;
|
||||
u16 pad;
|
||||
/* OUT parameters */
|
||||
u32 version;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(gnttab_get_version);
|
||||
|
||||
/*
|
||||
* Issue one or more cache maintenance operations on a portion of a
|
||||
* page granted to the calling domain by a foreign domain.
|
||||
*/
|
||||
#define GNTTABOP_cache_flush 12
|
||||
struct gnttab_cache_flush {
|
||||
union {
|
||||
u64 dev_bus_addr;
|
||||
grant_ref_t ref;
|
||||
} a;
|
||||
u16 offset; /* offset from start of grant */
|
||||
u16 length; /* size within the grant */
|
||||
#define GNTTAB_CACHE_CLEAN (1 << 0)
|
||||
#define GNTTAB_CACHE_INVAL (1 << 1)
|
||||
#define GNTTAB_CACHE_SOURCE_GREF (1 << 31)
|
||||
u32 op;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(gnttab_cache_flush);
|
||||
|
||||
/*
|
||||
* Bitfield values for update_pin_status.flags.
|
||||
*/
|
||||
/* Map the grant entry for access by I/O devices. */
|
||||
#define _GNTMAP_device_map (0)
|
||||
#define GNTMAP_device_map (1 << _GNTMAP_device_map)
|
||||
/* Map the grant entry for access by host CPUs. */
|
||||
#define _GNTMAP_host_map (1)
|
||||
#define GNTMAP_host_map (1 << _GNTMAP_host_map)
|
||||
/* Accesses to the granted frame will be restricted to read-only access. */
|
||||
#define _GNTMAP_readonly (2)
|
||||
#define GNTMAP_readonly (1 << _GNTMAP_readonly)
|
||||
/*
|
||||
* GNTMAP_host_map subflag:
|
||||
* 0 => The host mapping is usable only by the guest OS.
|
||||
* 1 => The host mapping is usable by guest OS + current application.
|
||||
*/
|
||||
#define _GNTMAP_application_map (3)
|
||||
#define GNTMAP_application_map (1 << _GNTMAP_application_map)
|
||||
|
||||
/*
|
||||
* GNTMAP_contains_pte subflag:
|
||||
* 0 => This map request contains a host virtual address.
|
||||
* 1 => This map request contains the machine addess of the PTE to update.
|
||||
*/
|
||||
#define _GNTMAP_contains_pte (4)
|
||||
#define GNTMAP_contains_pte (1 << _GNTMAP_contains_pte)
|
||||
|
||||
/*
|
||||
* Bits to be placed in guest kernel available PTE bits (architecture
|
||||
* dependent; only supported when XENFEAT_gnttab_map_avail_bits is set).
|
||||
*/
|
||||
#define _GNTMAP_guest_avail0 (16)
|
||||
#define GNTMAP_guest_avail_mask ((u32)~0 << _GNTMAP_guest_avail0)
|
||||
|
||||
/*
|
||||
* Values for error status returns. All errors are -ve.
|
||||
*/
|
||||
#define GNTST_okay (0) /* Normal return. */
|
||||
#define GNTST_general_error (-1) /* General undefined error. */
|
||||
#define GNTST_bad_domain (-2) /* Unrecognsed domain id. */
|
||||
#define GNTST_bad_gntref (-3) /* Unrecognised or inappropriate gntref. */
|
||||
#define GNTST_bad_handle (-4) /* Unrecognised or inappropriate handle. */
|
||||
#define GNTST_bad_virt_addr (-5) /* Inappropriate virtual address to map. */
|
||||
#define GNTST_bad_dev_addr (-6) /* Inappropriate device address to unmap.*/
|
||||
#define GNTST_no_device_space (-7) /* Out of space in I/O MMU. */
|
||||
#define GNTST_permission_denied (-8) /* Not enough privilege for operation. */
|
||||
#define GNTST_bad_page (-9) /* Specified page was invalid for op. */
|
||||
#define GNTST_bad_copy_arg (-10) /* copy arguments cross page boundary. */
|
||||
#define GNTST_address_too_big (-11) /* transfer page address too large. */
|
||||
#define GNTST_eagain (-12) /* Operation not done; try again. */
|
||||
|
||||
#define GNTTABOP_error_msgs { \
|
||||
"okay", \
|
||||
"undefined error", \
|
||||
"unrecognised domain id", \
|
||||
"invalid grant reference", \
|
||||
"invalid mapping handle", \
|
||||
"invalid virtual address", \
|
||||
"invalid device address", \
|
||||
"no spare translation slot in the I/O MMU", \
|
||||
"permission denied", \
|
||||
"bad page", \
|
||||
"copy arguments cross page boundary", \
|
||||
"page address size too large", \
|
||||
"operation not done; try again" \
|
||||
}
|
||||
|
||||
#endif /* __XEN_PUBLIC_GRANT_TABLE_H__ */
|
55
include/xen/interface/hvm/hvm_op.h
Normal file
55
include/xen/interface/hvm/hvm_op.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* hvm_op.h
|
||||
*
|
||||
* Copyright (c) 2007, Keir Fraser
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PUBLIC_HVM_HVM_OP_H__
|
||||
#define __XEN_PUBLIC_HVM_HVM_OP_H__
|
||||
|
||||
/* Get/set subcommands: the second argument of the hypercall is a
|
||||
* pointer to a xen_hvm_param struct.
|
||||
*/
|
||||
#define HVMOP_set_param 0
|
||||
#define HVMOP_get_param 1
|
||||
struct xen_hvm_param {
|
||||
domid_t domid; /* IN */
|
||||
u32 index; /* IN */
|
||||
u64 value; /* IN/OUT */
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(xen_hvm_param);
|
||||
|
||||
/* Hint from PV drivers for pagetable destruction. */
|
||||
#define HVMOP_pagetable_dying 9
|
||||
struct xen_hvm_pagetable_dying {
|
||||
/* Domain with a pagetable about to be destroyed. */
|
||||
domid_t domid;
|
||||
/* guest physical address of the toplevel pagetable dying */
|
||||
aligned_u64 gpa;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(xen_hvm_pagetable_dying);
|
||||
|
||||
enum hvmmem_type_t {
|
||||
HVMMEM_ram_rw, /* Normal read/write guest RAM */
|
||||
HVMMEM_ram_ro, /* Read-only; writes are discarded */
|
||||
HVMMEM_mmio_dm, /* Reads and write go to the device model */
|
||||
};
|
||||
|
||||
#define HVMOP_get_mem_type 15
|
||||
/* Return hvmmem_type_t for the specified pfn. */
|
||||
struct xen_hvm_get_mem_type {
|
||||
/* Domain to be queried. */
|
||||
domid_t domid;
|
||||
/* OUT variable. */
|
||||
u16 mem_type;
|
||||
u16 pad[2]; /* align next field on 8-byte boundary */
|
||||
/* IN variable. */
|
||||
u64 pfn;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(xen_hvm_get_mem_type);
|
||||
|
||||
#endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */
|
116
include/xen/interface/hvm/params.h
Normal file
116
include/xen/interface/hvm/params.h
Normal file
@ -0,0 +1,116 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* params.h
|
||||
*
|
||||
* HVM parameters. HVM (Hardware Virtual Machine) is the type of instance
|
||||
* that mimics bare-metal server setup which provides better hardware
|
||||
* isolation.
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PUBLIC_HVM_PARAMS_H__
|
||||
#define __XEN_PUBLIC_HVM_PARAMS_H__
|
||||
|
||||
#include <xen/interface/hvm/hvm_op.h>
|
||||
|
||||
/*
|
||||
* Parameter space for HVMOP_{set,get}_param.
|
||||
*/
|
||||
|
||||
#define HVM_PARAM_CALLBACK_IRQ 0
|
||||
/*
|
||||
* How should CPU0 event-channel notifications be delivered?
|
||||
*
|
||||
* If val == 0 then CPU0 event-channel notifications are not delivered.
|
||||
* If val != 0, val[63:56] encodes the type, as follows:
|
||||
*/
|
||||
|
||||
#define HVM_PARAM_CALLBACK_TYPE_GSI 0
|
||||
/*
|
||||
* val[55:0] is a delivery GSI. GSI 0 cannot be used, as it aliases val == 0,
|
||||
* and disables all notifications.
|
||||
*/
|
||||
|
||||
#define HVM_PARAM_CALLBACK_TYPE_PCI_INTX 1
|
||||
/*
|
||||
* val[55:0] is a delivery PCI INTx line:
|
||||
* Domain = val[47:32], Bus = val[31:16] DevFn = val[15:8], IntX = val[1:0]
|
||||
*/
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#define HVM_PARAM_CALLBACK_TYPE_VECTOR 2
|
||||
/*
|
||||
* val[7:0] is a vector number. Check for XENFEAT_hvm_callback_vector to know
|
||||
* if this delivery method is available.
|
||||
*/
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
#define HVM_PARAM_CALLBACK_TYPE_PPI 2
|
||||
/*
|
||||
* val[55:16] needs to be zero.
|
||||
* val[15:8] is interrupt flag of the PPI used by event-channel:
|
||||
* bit 8: the PPI is edge(1) or level(0) triggered
|
||||
* bit 9: the PPI is active low(1) or high(0)
|
||||
* val[7:0] is a PPI number used by event-channel.
|
||||
* This is only used by ARM/ARM64 and masking/eoi the interrupt associated to
|
||||
* the notification is handled by the interrupt controller.
|
||||
*/
|
||||
#endif
|
||||
|
||||
#define HVM_PARAM_STORE_PFN 1
|
||||
#define HVM_PARAM_STORE_EVTCHN 2
|
||||
|
||||
#define HVM_PARAM_PAE_ENABLED 4
|
||||
|
||||
#define HVM_PARAM_IOREQ_PFN 5
|
||||
|
||||
#define HVM_PARAM_BUFIOREQ_PFN 6
|
||||
|
||||
/*
|
||||
* Set mode for virtual timers (currently x86 only):
|
||||
* delay_for_missed_ticks (default):
|
||||
* Do not advance a vcpu's time beyond the correct delivery time for
|
||||
* interrupts that have been missed due to preemption. Deliver missed
|
||||
* interrupts when the vcpu is rescheduled and advance the vcpu's virtual
|
||||
* time stepwise for each one.
|
||||
* no_delay_for_missed_ticks:
|
||||
* As above, missed interrupts are delivered, but guest time always tracks
|
||||
* wallclock (i.e., real) time while doing so.
|
||||
* no_missed_ticks_pending:
|
||||
* No missed interrupts are held pending. Instead, to ensure ticks are
|
||||
* delivered at some non-zero rate, if we detect missed ticks then the
|
||||
* internal tick alarm is not disabled if the VCPU is preempted during the
|
||||
* next tick period.
|
||||
* one_missed_tick_pending:
|
||||
* Missed interrupts are collapsed together and delivered as one 'late tick'.
|
||||
* Guest time always tracks wallclock (i.e., real) time.
|
||||
*/
|
||||
#define HVM_PARAM_TIMER_MODE 10
|
||||
#define HVMPTM_delay_for_missed_ticks 0
|
||||
#define HVMPTM_no_delay_for_missed_ticks 1
|
||||
#define HVMPTM_no_missed_ticks_pending 2
|
||||
#define HVMPTM_one_missed_tick_pending 3
|
||||
|
||||
/* Boolean: Enable virtual HPET (high-precision event timer)? (x86-only) */
|
||||
#define HVM_PARAM_HPET_ENABLED 11
|
||||
|
||||
/* Identity-map page directory used by Intel EPT when CR0.PG=0. */
|
||||
#define HVM_PARAM_IDENT_PT 12
|
||||
|
||||
/* Device Model domain, defaults to 0. */
|
||||
#define HVM_PARAM_DM_DOMAIN 13
|
||||
|
||||
/* ACPI S state: currently support S0 and S3 on x86. */
|
||||
#define HVM_PARAM_ACPI_S_STATE 14
|
||||
|
||||
/* TSS used on Intel when CR0.PE=0. */
|
||||
#define HVM_PARAM_VM86_TSS 15
|
||||
|
||||
/* Boolean: Enable aligning all periodic vpts to reduce interrupts */
|
||||
#define HVM_PARAM_VPT_ALIGN 16
|
||||
|
||||
/* Console debug shared memory ring and event channel */
|
||||
#define HVM_PARAM_CONSOLE_PFN 17
|
||||
#define HVM_PARAM_CONSOLE_EVTCHN 18
|
||||
|
||||
#define HVM_NR_PARAMS 19
|
||||
|
||||
#endif /* __XEN_PUBLIC_HVM_PARAMS_H__ */
|
701
include/xen/interface/io/blkif.h
Normal file
701
include/xen/interface/io/blkif.h
Normal file
@ -0,0 +1,701 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* blkif.h
|
||||
*
|
||||
* Unified block-device I/O interface for Xen guest OSes.
|
||||
*
|
||||
* Copyright (c) 2003-2004, Keir Fraser
|
||||
* Copyright (c) 2012, Spectra Logic Corporation
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PUBLIC_IO_BLKIF_H__
|
||||
#define __XEN_PUBLIC_IO_BLKIF_H__
|
||||
|
||||
#include "ring.h"
|
||||
#include "../grant_table.h"
|
||||
|
||||
/*
|
||||
* Front->back notifications: When enqueuing a new request, sending a
|
||||
* notification can be made conditional on req_event (i.e., the generic
|
||||
* hold-off mechanism provided by the ring macros). Backends must set
|
||||
* req_event appropriately (e.g., using RING_FINAL_CHECK_FOR_REQUESTS()).
|
||||
*
|
||||
* Back->front notifications: When enqueuing a new response, sending a
|
||||
* notification can be made conditional on rsp_event (i.e., the generic
|
||||
* hold-off mechanism provided by the ring macros). Frontends must set
|
||||
* rsp_event appropriately (e.g., using RING_FINAL_CHECK_FOR_RESPONSES()).
|
||||
*/
|
||||
|
||||
#ifndef blkif_vdev_t
|
||||
#define blkif_vdev_t u16
|
||||
#endif
|
||||
#define blkif_sector_t u64
|
||||
|
||||
/*
|
||||
* Feature and Parameter Negotiation
|
||||
* =================================
|
||||
* The two halves of a Xen block driver utilize nodes within the XenStore to
|
||||
* communicate capabilities and to negotiate operating parameters. This
|
||||
* section enumerates these nodes which reside in the respective front and
|
||||
* backend portions of the XenStore, following the XenBus convention.
|
||||
*
|
||||
* All data in the XenStore is stored as strings. Nodes specifying numeric
|
||||
* values are encoded in decimal. Integer value ranges listed below are
|
||||
* expressed as fixed sized integer types capable of storing the conversion
|
||||
* of a properly formated node string, without loss of information.
|
||||
*
|
||||
* Any specified default value is in effect if the corresponding XenBus node
|
||||
* is not present in the XenStore.
|
||||
*
|
||||
* XenStore nodes in sections marked "PRIVATE" are solely for use by the
|
||||
* driver side whose XenBus tree contains them.
|
||||
*
|
||||
* XenStore nodes marked "DEPRECATED" in their notes section should only be
|
||||
* used to provide interoperability with legacy implementations.
|
||||
*
|
||||
* See the XenBus state transition diagram below for details on when XenBus
|
||||
* nodes must be published and when they can be queried.
|
||||
*
|
||||
*****************************************************************************
|
||||
* Backend XenBus Nodes
|
||||
*****************************************************************************
|
||||
*
|
||||
*------------------ Backend Device Identification (PRIVATE) ------------------
|
||||
*
|
||||
* mode
|
||||
* Values: "r" (read only), "w" (writable)
|
||||
*
|
||||
* The read or write access permissions to the backing store to be
|
||||
* granted to the frontend.
|
||||
*
|
||||
* params
|
||||
* Values: string
|
||||
*
|
||||
* A free formatted string providing sufficient information for the
|
||||
* hotplug script to attach the device and provide a suitable
|
||||
* handler (ie: a block device) for blkback to use.
|
||||
*
|
||||
* physical-device
|
||||
* Values: "MAJOR:MINOR"
|
||||
* Notes: 11
|
||||
*
|
||||
* MAJOR and MINOR are the major number and minor number of the
|
||||
* backing device respectively.
|
||||
*
|
||||
* physical-device-path
|
||||
* Values: path string
|
||||
*
|
||||
* A string that contains the absolute path to the disk image. On
|
||||
* NetBSD and Linux this is always a block device, while on FreeBSD
|
||||
* it can be either a block device or a regular file.
|
||||
*
|
||||
* type
|
||||
* Values: "file", "phy", "tap"
|
||||
*
|
||||
* The type of the backing device/object.
|
||||
*
|
||||
*
|
||||
* direct-io-safe
|
||||
* Values: 0/1 (boolean)
|
||||
* Default Value: 0
|
||||
*
|
||||
* The underlying storage is not affected by the direct IO memory
|
||||
* lifetime bug. See:
|
||||
* http://lists.xen.org/archives/html/xen-devel/2012-12/msg01154.html
|
||||
*
|
||||
* Therefore this option gives the backend permission to use
|
||||
* O_DIRECT, notwithstanding that bug.
|
||||
*
|
||||
* That is, if this option is enabled, use of O_DIRECT is safe,
|
||||
* in circumstances where we would normally have avoided it as a
|
||||
* workaround for that bug. This option is not relevant for all
|
||||
* backends, and even not necessarily supported for those for
|
||||
* which it is relevant. A backend which knows that it is not
|
||||
* affected by the bug can ignore this option.
|
||||
*
|
||||
* This option doesn't require a backend to use O_DIRECT, so it
|
||||
* should not be used to try to control the caching behaviour.
|
||||
*
|
||||
*--------------------------------- Features ---------------------------------
|
||||
*
|
||||
* feature-barrier
|
||||
* Values: 0/1 (boolean)
|
||||
* Default Value: 0
|
||||
*
|
||||
* A value of "1" indicates that the backend can process requests
|
||||
* containing the BLKIF_OP_WRITE_BARRIER request opcode. Requests
|
||||
* of this type may still be returned at any time with the
|
||||
* BLKIF_RSP_EOPNOTSUPP result code.
|
||||
*
|
||||
* feature-flush-cache
|
||||
* Values: 0/1 (boolean)
|
||||
* Default Value: 0
|
||||
*
|
||||
* A value of "1" indicates that the backend can process requests
|
||||
* containing the BLKIF_OP_FLUSH_DISKCACHE request opcode. Requests
|
||||
* of this type may still be returned at any time with the
|
||||
* BLKIF_RSP_EOPNOTSUPP result code.
|
||||
*
|
||||
* feature-discard
|
||||
* Values: 0/1 (boolean)
|
||||
* Default Value: 0
|
||||
*
|
||||
* A value of "1" indicates that the backend can process requests
|
||||
* containing the BLKIF_OP_DISCARD request opcode. Requests
|
||||
* of this type may still be returned at any time with the
|
||||
* BLKIF_RSP_EOPNOTSUPP result code.
|
||||
*
|
||||
* feature-persistent
|
||||
* Values: 0/1 (boolean)
|
||||
* Default Value: 0
|
||||
* Notes: 7
|
||||
*
|
||||
* A value of "1" indicates that the backend can keep the grants used
|
||||
* by the frontend driver mapped, so the same set of grants should be
|
||||
* used in all transactions. The maximum number of grants the backend
|
||||
* can map persistently depends on the implementation, but ideally it
|
||||
* should be RING_SIZE * BLKIF_MAX_SEGMENTS_PER_REQUEST. Using this
|
||||
* feature the backend doesn't need to unmap each grant, preventing
|
||||
* costly TLB flushes. The backend driver should only map grants
|
||||
* persistently if the frontend supports it. If a backend driver chooses
|
||||
* to use the persistent protocol when the frontend doesn't support it,
|
||||
* it will probably hit the maximum number of persistently mapped grants
|
||||
* (due to the fact that the frontend won't be reusing the same grants),
|
||||
* and fall back to non-persistent mode. Backend implementations may
|
||||
* shrink or expand the number of persistently mapped grants without
|
||||
* notifying the frontend depending on memory constraints (this might
|
||||
* cause a performance degradation).
|
||||
*
|
||||
* If a backend driver wants to limit the maximum number of persistently
|
||||
* mapped grants to a value less than RING_SIZE *
|
||||
* BLKIF_MAX_SEGMENTS_PER_REQUEST a LRU strategy should be used to
|
||||
* discard the grants that are less commonly used. Using a LRU in the
|
||||
* backend driver paired with a LIFO queue in the frontend will
|
||||
* allow us to have better performance in this scenario.
|
||||
*
|
||||
*----------------------- Request Transport Parameters ------------------------
|
||||
*
|
||||
* max-ring-page-order
|
||||
* Values: <uint32_t>
|
||||
* Default Value: 0
|
||||
* Notes: 1, 3
|
||||
*
|
||||
* The maximum supported size of the request ring buffer in units of
|
||||
* lb(machine pages). (e.g. 0 == 1 page, 1 = 2 pages, 2 == 4 pages,
|
||||
* etc.).
|
||||
*
|
||||
* max-ring-pages
|
||||
* Values: <uint32_t>
|
||||
* Default Value: 1
|
||||
* Notes: DEPRECATED, 2, 3
|
||||
*
|
||||
* The maximum supported size of the request ring buffer in units of
|
||||
* machine pages. The value must be a power of 2.
|
||||
*
|
||||
*------------------------- Backend Device Properties -------------------------
|
||||
*
|
||||
* discard-enable
|
||||
* Values: 0/1 (boolean)
|
||||
* Default Value: 1
|
||||
*
|
||||
* This optional property, set by the toolstack, instructs the backend
|
||||
* to offer (or not to offer) discard to the frontend. If the property
|
||||
* is missing the backend should offer discard if the backing storage
|
||||
* actually supports it.
|
||||
*
|
||||
* discard-alignment
|
||||
* Values: <uint32_t>
|
||||
* Default Value: 0
|
||||
* Notes: 4, 5
|
||||
*
|
||||
* The offset, in bytes from the beginning of the virtual block device,
|
||||
* to the first, addressable, discard extent on the underlying device.
|
||||
*
|
||||
* discard-granularity
|
||||
* Values: <uint32_t>
|
||||
* Default Value: <"sector-size">
|
||||
* Notes: 4
|
||||
*
|
||||
* The size, in bytes, of the individually addressable discard extents
|
||||
* of the underlying device.
|
||||
*
|
||||
* discard-secure
|
||||
* Values: 0/1 (boolean)
|
||||
* Default Value: 0
|
||||
* Notes: 10
|
||||
*
|
||||
* A value of "1" indicates that the backend can process BLKIF_OP_DISCARD
|
||||
* requests with the BLKIF_DISCARD_SECURE flag set.
|
||||
*
|
||||
* info
|
||||
* Values: <uint32_t> (bitmap)
|
||||
*
|
||||
* A collection of bit flags describing attributes of the backing
|
||||
* device. The VDISK_* macros define the meaning of each bit
|
||||
* location.
|
||||
*
|
||||
* sector-size
|
||||
* Values: <uint32_t>
|
||||
*
|
||||
* The logical block size, in bytes, of the underlying storage. This
|
||||
* must be a power of two with a minimum value of 512.
|
||||
*
|
||||
* NOTE: Because of implementation bugs in some frontends this must be
|
||||
* set to 512, unless the frontend advertizes a non-zero value
|
||||
* in its "feature-large-sector-size" xenbus node. (See below).
|
||||
*
|
||||
* physical-sector-size
|
||||
* Values: <uint32_t>
|
||||
* Default Value: <"sector-size">
|
||||
*
|
||||
* The physical block size, in bytes, of the backend storage. This
|
||||
* must be an integer multiple of "sector-size".
|
||||
*
|
||||
* sectors
|
||||
* Values: <u64>
|
||||
*
|
||||
* The size of the backend device, expressed in units of "sector-size".
|
||||
* The product of "sector-size" and "sectors" must also be an integer
|
||||
* multiple of "physical-sector-size", if that node is present.
|
||||
*
|
||||
*****************************************************************************
|
||||
* Frontend XenBus Nodes
|
||||
*****************************************************************************
|
||||
*
|
||||
*----------------------- Request Transport Parameters -----------------------
|
||||
*
|
||||
* event-channel
|
||||
* Values: <uint32_t>
|
||||
*
|
||||
* The identifier of the Xen event channel used to signal activity
|
||||
* in the ring buffer.
|
||||
*
|
||||
* ring-ref
|
||||
* Values: <uint32_t>
|
||||
* Notes: 6
|
||||
*
|
||||
* The Xen grant reference granting permission for the backend to map
|
||||
* the sole page in a single page sized ring buffer.
|
||||
*
|
||||
* ring-ref%u
|
||||
* Values: <uint32_t>
|
||||
* Notes: 6
|
||||
*
|
||||
* For a frontend providing a multi-page ring, a "number of ring pages"
|
||||
* sized list of nodes, each containing a Xen grant reference granting
|
||||
* permission for the backend to map the page of the ring located
|
||||
* at page index "%u". Page indexes are zero based.
|
||||
*
|
||||
* protocol
|
||||
* Values: string (XEN_IO_PROTO_ABI_*)
|
||||
* Default Value: XEN_IO_PROTO_ABI_NATIVE
|
||||
*
|
||||
* The machine ABI rules governing the format of all ring request and
|
||||
* response structures.
|
||||
*
|
||||
* ring-page-order
|
||||
* Values: <uint32_t>
|
||||
* Default Value: 0
|
||||
* Maximum Value: MAX(ffs(max-ring-pages) - 1, max-ring-page-order)
|
||||
* Notes: 1, 3
|
||||
*
|
||||
* The size of the frontend allocated request ring buffer in units
|
||||
* of lb(machine pages). (e.g. 0 == 1 page, 1 = 2 pages, 2 == 4 pages,
|
||||
* etc.).
|
||||
*
|
||||
* num-ring-pages
|
||||
* Values: <uint32_t>
|
||||
* Default Value: 1
|
||||
* Maximum Value: MAX(max-ring-pages,(0x1 << max-ring-page-order))
|
||||
* Notes: DEPRECATED, 2, 3
|
||||
*
|
||||
* The size of the frontend allocated request ring buffer in units of
|
||||
* machine pages. The value must be a power of 2.
|
||||
*
|
||||
*--------------------------------- Features ---------------------------------
|
||||
*
|
||||
* feature-persistent
|
||||
* Values: 0/1 (boolean)
|
||||
* Default Value: 0
|
||||
* Notes: 7, 8, 9
|
||||
*
|
||||
* A value of "1" indicates that the frontend will reuse the same grants
|
||||
* for all transactions, allowing the backend to map them with write
|
||||
* access (even when it should be read-only). If the frontend hits the
|
||||
* maximum number of allowed persistently mapped grants, it can fallback
|
||||
* to non persistent mode. This will cause a performance degradation,
|
||||
* since the the backend driver will still try to map those grants
|
||||
* persistently. Since the persistent grants protocol is compatible with
|
||||
* the previous protocol, a frontend driver can choose to work in
|
||||
* persistent mode even when the backend doesn't support it.
|
||||
*
|
||||
* It is recommended that the frontend driver stores the persistently
|
||||
* mapped grants in a LIFO queue, so a subset of all persistently mapped
|
||||
* grants gets used commonly. This is done in case the backend driver
|
||||
* decides to limit the maximum number of persistently mapped grants
|
||||
* to a value less than RING_SIZE * BLKIF_MAX_SEGMENTS_PER_REQUEST.
|
||||
*
|
||||
* feature-large-sector-size
|
||||
* Values: 0/1 (boolean)
|
||||
* Default Value: 0
|
||||
*
|
||||
* A value of "1" indicates that the frontend will correctly supply and
|
||||
* interpret all sector-based quantities in terms of the "sector-size"
|
||||
* value supplied in the backend info, whatever that may be set to.
|
||||
* If this node is not present or its value is "0" then it is assumed
|
||||
* that the frontend requires that the logical block size is 512 as it
|
||||
* is hardcoded (which is the case in some frontend implementations).
|
||||
*
|
||||
*------------------------- Virtual Device Properties -------------------------
|
||||
*
|
||||
* device-type
|
||||
* Values: "disk", "cdrom", "floppy", etc.
|
||||
*
|
||||
* virtual-device
|
||||
* Values: <uint32_t>
|
||||
*
|
||||
* A value indicating the physical device to virtualize within the
|
||||
* frontend's domain. (e.g. "The first ATA disk", "The third SCSI
|
||||
* disk", etc.)
|
||||
*
|
||||
* See docs/misc/vbd-interface.txt for details on the format of this
|
||||
* value.
|
||||
*
|
||||
* Notes
|
||||
* -----
|
||||
* (1) Multi-page ring buffer scheme first developed in the Citrix XenServer
|
||||
* PV drivers.
|
||||
* (2) Multi-page ring buffer scheme first used in some RedHat distributions
|
||||
* including a distribution deployed on certain nodes of the Amazon
|
||||
* EC2 cluster.
|
||||
* (3) Support for multi-page ring buffers was implemented independently,
|
||||
* in slightly different forms, by both Citrix and RedHat/Amazon.
|
||||
* For full interoperability, block front and backends should publish
|
||||
* identical ring parameters, adjusted for unit differences, to the
|
||||
* XenStore nodes used in both schemes.
|
||||
* (4) Devices that support discard functionality may internally allocate space
|
||||
* (discardable extents) in units that are larger than the exported logical
|
||||
* block size. If the backing device has such discardable extents the
|
||||
* backend should provide both discard-granularity and discard-alignment.
|
||||
* Providing just one of the two may be considered an error by the frontend.
|
||||
* Backends supporting discard should include discard-granularity and
|
||||
* discard-alignment even if it supports discarding individual sectors.
|
||||
* Frontends should assume discard-alignment == 0 and discard-granularity
|
||||
* == sector size if these keys are missing.
|
||||
* (5) The discard-alignment parameter allows a physical device to be
|
||||
* partitioned into virtual devices that do not necessarily begin or
|
||||
* end on a discardable extent boundary.
|
||||
* (6) When there is only a single page allocated to the request ring,
|
||||
* 'ring-ref' is used to communicate the grant reference for this
|
||||
* page to the backend. When using a multi-page ring, the 'ring-ref'
|
||||
* node is not created. Instead 'ring-ref0' - 'ring-refN' are used.
|
||||
* (7) When using persistent grants data has to be copied from/to the page
|
||||
* where the grant is currently mapped. The overhead of doing this copy
|
||||
* however doesn't suppress the speed improvement of not having to unmap
|
||||
* the grants.
|
||||
* (8) The frontend driver has to allow the backend driver to map all grants
|
||||
* with write access, even when they should be mapped read-only, since
|
||||
* further requests may reuse these grants and require write permissions.
|
||||
* (9) Linux implementation doesn't have a limit on the maximum number of
|
||||
* grants that can be persistently mapped in the frontend driver, but
|
||||
* due to the frontent driver implementation it should never be bigger
|
||||
* than RING_SIZE * BLKIF_MAX_SEGMENTS_PER_REQUEST.
|
||||
*(10) The discard-secure property may be present and will be set to 1 if the
|
||||
* backing device supports secure discard.
|
||||
*(11) Only used by Linux and NetBSD.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Multiple hardware queues/rings:
|
||||
* If supported, the backend will write the key "multi-queue-max-queues" to
|
||||
* the directory for that vbd, and set its value to the maximum supported
|
||||
* number of queues.
|
||||
* Frontends that are aware of this feature and wish to use it can write the
|
||||
* key "multi-queue-num-queues" with the number they wish to use, which must be
|
||||
* greater than zero, and no more than the value reported by the backend in
|
||||
* "multi-queue-max-queues".
|
||||
*
|
||||
* For frontends requesting just one queue, the usual event-channel and
|
||||
* ring-ref keys are written as before, simplifying the backend processing
|
||||
* to avoid distinguishing between a frontend that doesn't understand the
|
||||
* multi-queue feature, and one that does, but requested only one queue.
|
||||
*
|
||||
* Frontends requesting two or more queues must not write the toplevel
|
||||
* event-channel and ring-ref keys, instead writing those keys under sub-keys
|
||||
* having the name "queue-N" where N is the integer ID of the queue/ring for
|
||||
* which those keys belong. Queues are indexed from zero.
|
||||
* For example, a frontend with two queues must write the following set of
|
||||
* queue-related keys:
|
||||
*
|
||||
* /local/domain/1/device/vbd/0/multi-queue-num-queues = "2"
|
||||
* /local/domain/1/device/vbd/0/queue-0 = ""
|
||||
* /local/domain/1/device/vbd/0/queue-0/ring-ref = "<ring-ref#0>"
|
||||
* /local/domain/1/device/vbd/0/queue-0/event-channel = "<evtchn#0>"
|
||||
* /local/domain/1/device/vbd/0/queue-1 = ""
|
||||
* /local/domain/1/device/vbd/0/queue-1/ring-ref = "<ring-ref#1>"
|
||||
* /local/domain/1/device/vbd/0/queue-1/event-channel = "<evtchn#1>"
|
||||
*
|
||||
* It is also possible to use multiple queues/rings together with
|
||||
* feature multi-page ring buffer.
|
||||
* For example, a frontend requests two queues/rings and the size of each ring
|
||||
* buffer is two pages must write the following set of related keys:
|
||||
*
|
||||
* /local/domain/1/device/vbd/0/multi-queue-num-queues = "2"
|
||||
* /local/domain/1/device/vbd/0/ring-page-order = "1"
|
||||
* /local/domain/1/device/vbd/0/queue-0 = ""
|
||||
* /local/domain/1/device/vbd/0/queue-0/ring-ref0 = "<ring-ref#0>"
|
||||
* /local/domain/1/device/vbd/0/queue-0/ring-ref1 = "<ring-ref#1>"
|
||||
* /local/domain/1/device/vbd/0/queue-0/event-channel = "<evtchn#0>"
|
||||
* /local/domain/1/device/vbd/0/queue-1 = ""
|
||||
* /local/domain/1/device/vbd/0/queue-1/ring-ref0 = "<ring-ref#2>"
|
||||
* /local/domain/1/device/vbd/0/queue-1/ring-ref1 = "<ring-ref#3>"
|
||||
* /local/domain/1/device/vbd/0/queue-1/event-channel = "<evtchn#1>"
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* STATE DIAGRAMS
|
||||
*
|
||||
*****************************************************************************
|
||||
* Startup *
|
||||
*****************************************************************************
|
||||
*
|
||||
* Tool stack creates front and back nodes with state XenbusStateInitialising.
|
||||
*
|
||||
* Front Back
|
||||
* ================================= =====================================
|
||||
* XenbusStateInitialising XenbusStateInitialising
|
||||
* o Query virtual device o Query backend device identification
|
||||
* properties. data.
|
||||
* o Setup OS device instance. o Open and validate backend device.
|
||||
* o Publish backend features and
|
||||
* transport parameters.
|
||||
* |
|
||||
* |
|
||||
* V
|
||||
* XenbusStateInitWait
|
||||
*
|
||||
* o Query backend features and
|
||||
* transport parameters.
|
||||
* o Allocate and initialize the
|
||||
* request ring.
|
||||
* o Publish transport parameters
|
||||
* that will be in effect during
|
||||
* this connection.
|
||||
* |
|
||||
* |
|
||||
* V
|
||||
* XenbusStateInitialised
|
||||
*
|
||||
* o Query frontend transport parameters.
|
||||
* o Connect to the request ring and
|
||||
* event channel.
|
||||
* o Publish backend device properties.
|
||||
* |
|
||||
* |
|
||||
* V
|
||||
* XenbusStateConnected
|
||||
*
|
||||
* o Query backend device properties.
|
||||
* o Finalize OS virtual device
|
||||
* instance.
|
||||
* |
|
||||
* |
|
||||
* V
|
||||
* XenbusStateConnected
|
||||
*
|
||||
* Note: Drivers that do not support any optional features, or the negotiation
|
||||
* of transport parameters, can skip certain states in the state machine:
|
||||
*
|
||||
* o A frontend may transition to XenbusStateInitialised without
|
||||
* waiting for the backend to enter XenbusStateInitWait. In this
|
||||
* case, default transport parameters are in effect and any
|
||||
* transport parameters published by the frontend must contain
|
||||
* their default values.
|
||||
*
|
||||
* o A backend may transition to XenbusStateInitialised, bypassing
|
||||
* XenbusStateInitWait, without waiting for the frontend to first
|
||||
* enter the XenbusStateInitialised state. In this case, default
|
||||
* transport parameters are in effect and any transport parameters
|
||||
* published by the backend must contain their default values.
|
||||
*
|
||||
* Drivers that support optional features and/or transport parameter
|
||||
* negotiation must tolerate these additional state transition paths.
|
||||
* In general this means performing the work of any skipped state
|
||||
* transition, if it has not already been performed, in addition to the
|
||||
* work associated with entry into the current state.
|
||||
*/
|
||||
|
||||
/*
|
||||
* REQUEST CODES.
|
||||
*/
|
||||
#define BLKIF_OP_READ 0
|
||||
#define BLKIF_OP_WRITE 1
|
||||
/*
|
||||
* All writes issued prior to a request with the BLKIF_OP_WRITE_BARRIER
|
||||
* operation code ("barrier request") must be completed prior to the
|
||||
* execution of the barrier request. All writes issued after the barrier
|
||||
* request must not execute until after the completion of the barrier request.
|
||||
*
|
||||
* Optional. See "feature-barrier" XenBus node documentation above.
|
||||
*/
|
||||
#define BLKIF_OP_WRITE_BARRIER 2
|
||||
/*
|
||||
* Commit any uncommitted contents of the backing device's volatile cache
|
||||
* to stable storage.
|
||||
*
|
||||
* Optional. See "feature-flush-cache" XenBus node documentation above.
|
||||
*/
|
||||
#define BLKIF_OP_FLUSH_DISKCACHE 3
|
||||
/*
|
||||
* Used in SLES sources for device specific command packet
|
||||
* contained within the request. Reserved for that purpose.
|
||||
*/
|
||||
#define BLKIF_OP_RESERVED_1 4
|
||||
/*
|
||||
* Indicate to the backend device that a region of storage is no longer in
|
||||
* use, and may be discarded at any time without impact to the client. If
|
||||
* the BLKIF_DISCARD_SECURE flag is set on the request, all copies of the
|
||||
* discarded region on the device must be rendered unrecoverable before the
|
||||
* command returns.
|
||||
*
|
||||
* This operation is analogous to performing a trim (ATA) or unamp (SCSI),
|
||||
* command on a native device.
|
||||
*
|
||||
* More information about trim/unmap operations can be found at:
|
||||
* http://t13.org/Documents/UploadedDocuments/docs2008/
|
||||
* e07154r6-Data_Set_Management_Proposal_for_ATA-ACS2.doc
|
||||
* http://www.seagate.com/staticfiles/support/disc/manuals/
|
||||
* Interface%20manuals/100293068c.pdf
|
||||
*
|
||||
* Optional. See "feature-discard", "discard-alignment",
|
||||
* "discard-granularity", and "discard-secure" in the XenBus node
|
||||
* documentation above.
|
||||
*/
|
||||
#define BLKIF_OP_DISCARD 5
|
||||
|
||||
/*
|
||||
* Recognized if "feature-max-indirect-segments" in present in the backend
|
||||
* xenbus info. The "feature-max-indirect-segments" node contains the maximum
|
||||
* number of segments allowed by the backend per request. If the node is
|
||||
* present, the frontend might use blkif_request_indirect structs in order to
|
||||
* issue requests with more than BLKIF_MAX_SEGMENTS_PER_REQUEST (11). The
|
||||
* maximum number of indirect segments is fixed by the backend, but the
|
||||
* frontend can issue requests with any number of indirect segments as long as
|
||||
* it's less than the number provided by the backend. The indirect_grefs field
|
||||
* in blkif_request_indirect should be filled by the frontend with the
|
||||
* grant references of the pages that are holding the indirect segments.
|
||||
* These pages are filled with an array of blkif_request_segment that hold the
|
||||
* information about the segments. The number of indirect pages to use is
|
||||
* determined by the number of segments an indirect request contains. Every
|
||||
* indirect page can contain a maximum of
|
||||
* (PAGE_SIZE / sizeof(struct blkif_request_segment)) segments, so to
|
||||
* calculate the number of indirect pages to use we have to do
|
||||
* ceil(indirect_segments / (PAGE_SIZE / sizeof(struct blkif_request_segment))).
|
||||
*
|
||||
* If a backend does not recognize BLKIF_OP_INDIRECT, it should *not*
|
||||
* create the "feature-max-indirect-segments" node!
|
||||
*/
|
||||
#define BLKIF_OP_INDIRECT 6
|
||||
|
||||
/*
|
||||
* Maximum scatter/gather segments per request.
|
||||
* This is carefully chosen so that sizeof(blkif_ring_t) <= PAGE_SIZE.
|
||||
* NB. This could be 12 if the ring indexes weren't stored in the same page.
|
||||
*/
|
||||
#define BLKIF_MAX_SEGMENTS_PER_REQUEST 11
|
||||
|
||||
/*
|
||||
* Maximum number of indirect pages to use per request.
|
||||
*/
|
||||
#define BLKIF_MAX_INDIRECT_PAGES_PER_REQUEST 8
|
||||
|
||||
/*
|
||||
* NB. 'first_sect' and 'last_sect' in blkif_request_segment, as well as
|
||||
* 'sector_number' in blkif_request, blkif_request_discard and
|
||||
* blkif_request_indirect are sector-based quantities. See the description
|
||||
* of the "feature-large-sector-size" frontend xenbus node above for
|
||||
* more information.
|
||||
*/
|
||||
struct blkif_request_segment {
|
||||
grant_ref_t gref; /* reference to I/O buffer frame */
|
||||
/* @first_sect: first sector in frame to transfer (inclusive). */
|
||||
/* @last_sect: last sector in frame to transfer (inclusive). */
|
||||
u8 first_sect, last_sect;
|
||||
};
|
||||
|
||||
/*
|
||||
* Starting ring element for any I/O request.
|
||||
*/
|
||||
struct blkif_request {
|
||||
u8 operation; /* BLKIF_OP_??? */
|
||||
u8 nr_segments; /* number of segments */
|
||||
blkif_vdev_t handle; /* only for read/write requests */
|
||||
u64 id; /* private guest value, echoed in resp */
|
||||
blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
|
||||
struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
|
||||
};
|
||||
|
||||
/*
|
||||
* Cast to this structure when blkif_request.operation == BLKIF_OP_DISCARD
|
||||
* sizeof(struct blkif_request_discard) <= sizeof(struct blkif_request)
|
||||
*/
|
||||
struct blkif_request_discard {
|
||||
u8 operation; /* BLKIF_OP_DISCARD */
|
||||
u8 flag; /* BLKIF_DISCARD_SECURE or zero */
|
||||
#define BLKIF_DISCARD_SECURE (1 << 0) /* ignored if discard-secure=0 */
|
||||
blkif_vdev_t handle; /* same as for read/write requests */
|
||||
u64 id; /* private guest value, echoed in resp */
|
||||
blkif_sector_t sector_number;/* start sector idx on disk */
|
||||
u64 nr_sectors; /* number of contiguous sectors to discard*/
|
||||
};
|
||||
|
||||
struct blkif_request_indirect {
|
||||
u8 operation; /* BLKIF_OP_INDIRECT */
|
||||
u8 indirect_op; /* BLKIF_OP_{READ/WRITE} */
|
||||
u16 nr_segments; /* number of segments */
|
||||
u64 id; /* private guest value, echoed in resp */
|
||||
blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
|
||||
blkif_vdev_t handle; /* same as for read/write requests */
|
||||
grant_ref_t indirect_grefs[BLKIF_MAX_INDIRECT_PAGES_PER_REQUEST];
|
||||
#ifdef __i386__
|
||||
u64 pad; /* Make it 64 byte aligned on i386 */
|
||||
#endif
|
||||
};
|
||||
|
||||
struct blkif_response {
|
||||
u64 id; /* copied from request */
|
||||
u8 operation; /* copied from request */
|
||||
s16 status; /* BLKIF_RSP_??? */
|
||||
};
|
||||
|
||||
/*
|
||||
* STATUS RETURN CODES.
|
||||
*/
|
||||
/* Operation not supported (only happens on barrier writes). */
|
||||
#define BLKIF_RSP_EOPNOTSUPP -2
|
||||
/* Operation failed for some unspecified reason (-EIO). */
|
||||
#define BLKIF_RSP_ERROR -1
|
||||
/* Operation completed successfully. */
|
||||
#define BLKIF_RSP_OKAY 0
|
||||
|
||||
/*
|
||||
* Generate blkif ring structures and types.
|
||||
*/
|
||||
DEFINE_RING_TYPES(blkif, struct blkif_request, struct blkif_response);
|
||||
|
||||
#define VDISK_CDROM 0x1
|
||||
#define VDISK_REMOVABLE 0x2
|
||||
#define VDISK_READONLY 0x4
|
||||
|
||||
#endif /* __XEN_PUBLIC_IO_BLKIF_H__ */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
39
include/xen/interface/io/console.h
Normal file
39
include/xen/interface/io/console.h
Normal file
@ -0,0 +1,39 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* console.h
|
||||
*
|
||||
* Console I/O interface for Xen guest OSes.
|
||||
*
|
||||
* Copyright (c) 2005, Keir Fraser
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PUBLIC_IO_CONSOLE_H__
|
||||
#define __XEN_PUBLIC_IO_CONSOLE_H__
|
||||
|
||||
typedef u32 XENCONS_RING_IDX;
|
||||
|
||||
#define MASK_XENCONS_IDX(idx, ring) ((idx) & (sizeof(ring) - 1))
|
||||
|
||||
struct xencons_interface {
|
||||
char in[1024];
|
||||
char out[2048];
|
||||
XENCONS_RING_IDX in_cons, in_prod;
|
||||
XENCONS_RING_IDX out_cons, out_prod;
|
||||
};
|
||||
|
||||
#ifdef XEN_WANT_FLEX_CONSOLE_RING
|
||||
#include "ring.h"
|
||||
DEFINE_XEN_FLEX_RING(xencons);
|
||||
#endif
|
||||
|
||||
#endif /* __XEN_PUBLIC_IO_CONSOLE_H__ */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
28
include/xen/interface/io/protocols.h
Normal file
28
include/xen/interface/io/protocols.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* protocols.h
|
||||
*
|
||||
* Copyright (c) 2008, Keir Fraser
|
||||
*
|
||||
* Xen protocols, which are used as ABI rules governing the format of all
|
||||
* ring request and response structures.
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PROTOCOLS_H__
|
||||
#define __XEN_PROTOCOLS_H__
|
||||
|
||||
#define XEN_IO_PROTO_ABI_X86_32 "x86_32-abi"
|
||||
#define XEN_IO_PROTO_ABI_X86_64 "x86_64-abi"
|
||||
#define XEN_IO_PROTO_ABI_ARM "arm-abi"
|
||||
|
||||
#if defined(__i386__)
|
||||
# define XEN_IO_PROTO_ABI_NATIVE XEN_IO_PROTO_ABI_X86_32
|
||||
#elif defined(__x86_64__)
|
||||
# define XEN_IO_PROTO_ABI_NATIVE XEN_IO_PROTO_ABI_X86_64
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
# define XEN_IO_PROTO_ABI_NATIVE XEN_IO_PROTO_ABI_ARM
|
||||
#else
|
||||
# error arch fixup needed here
|
||||
#endif
|
||||
|
||||
#endif
|
462
include/xen/interface/io/ring.h
Normal file
462
include/xen/interface/io/ring.h
Normal file
@ -0,0 +1,462 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* ring.h
|
||||
*
|
||||
* Shared producer-consumer ring macros.
|
||||
*
|
||||
* Tim Deegan and Andrew Warfield November 2004.
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PUBLIC_IO_RING_H__
|
||||
#define __XEN_PUBLIC_IO_RING_H__
|
||||
|
||||
/*
|
||||
* When #include'ing this header, you need to provide the following
|
||||
* declaration upfront:
|
||||
* - standard integers types (u8, u16, etc)
|
||||
* They are provided by stdint.h of the standard headers.
|
||||
*
|
||||
* In addition, if you intend to use the FLEX macros, you also need to
|
||||
* provide the following, before invoking the FLEX macros:
|
||||
* - size_t
|
||||
* - memcpy
|
||||
* - grant_ref_t
|
||||
* These declarations are provided by string.h of the standard headers,
|
||||
* and grant_table.h from the Xen public headers.
|
||||
*/
|
||||
|
||||
#include <xen/interface/grant_table.h>
|
||||
|
||||
typedef unsigned int RING_IDX;
|
||||
|
||||
/* Round a 32-bit unsigned constant down to the nearest power of two. */
|
||||
#define __RD2(_x) (((_x) & 0x00000002) ? 0x2 : ((_x) & 0x1))
|
||||
#define __RD4(_x) (((_x) & 0x0000000c) ? __RD2((_x) >> 2) << 2 : __RD2(_x))
|
||||
#define __RD8(_x) (((_x) & 0x000000f0) ? __RD4((_x) >> 4) << 4 : __RD4(_x))
|
||||
#define __RD16(_x) (((_x) & 0x0000ff00) ? __RD8((_x) >> 8) << 8 : __RD8(_x))
|
||||
#define __RD32(_x) (((_x) & 0xffff0000) ? __RD16((_x) >> 16) << 16 : __RD16(_x))
|
||||
|
||||
/*
|
||||
* Calculate size of a shared ring, given the total available space for the
|
||||
* ring and indexes (_sz), and the name tag of the request/response structure.
|
||||
* A ring contains as many entries as will fit, rounded down to the nearest
|
||||
* power of two (so we can mask with (size-1) to loop around).
|
||||
*/
|
||||
#define __CONST_RING_SIZE(_s, _sz) \
|
||||
(__RD32(((_sz) - offsetof(struct _s##_sring, ring)) / \
|
||||
sizeof(((struct _s##_sring *)0)->ring[0])))
|
||||
/*
|
||||
* The same for passing in an actual pointer instead of a name tag.
|
||||
*/
|
||||
#define __RING_SIZE(_s, _sz) \
|
||||
(__RD32(((_sz) - (long)(_s)->ring + (long)(_s)) / sizeof((_s)->ring[0])))
|
||||
|
||||
/*
|
||||
* Macros to make the correct C datatypes for a new kind of ring.
|
||||
*
|
||||
* To make a new ring datatype, you need to have two message structures,
|
||||
* let's say request_t, and response_t already defined.
|
||||
*
|
||||
* In a header where you want the ring datatype declared, you then do:
|
||||
*
|
||||
* DEFINE_RING_TYPES(mytag, request_t, response_t);
|
||||
*
|
||||
* These expand out to give you a set of types, as you can see below.
|
||||
* The most important of these are:
|
||||
*
|
||||
* mytag_sring_t - The shared ring.
|
||||
* mytag_front_ring_t - The 'front' half of the ring.
|
||||
* mytag_back_ring_t - The 'back' half of the ring.
|
||||
*
|
||||
* To initialize a ring in your code you need to know the location and size
|
||||
* of the shared memory area (PAGE_SIZE, for instance). To initialise
|
||||
* the front half:
|
||||
*
|
||||
* mytag_front_ring_t front_ring;
|
||||
* SHARED_RING_INIT((mytag_sring_t *)shared_page);
|
||||
* FRONT_RING_INIT(&front_ring, (mytag_sring_t *)shared_page, PAGE_SIZE);
|
||||
*
|
||||
* Initializing the back follows similarly (note that only the front
|
||||
* initializes the shared ring):
|
||||
*
|
||||
* mytag_back_ring_t back_ring;
|
||||
* BACK_RING_INIT(&back_ring, (mytag_sring_t *)shared_page, PAGE_SIZE);
|
||||
*/
|
||||
|
||||
#define DEFINE_RING_TYPES(__name, __req_t, __rsp_t) \
|
||||
\
|
||||
/* Shared ring entry */ \
|
||||
union __name##_sring_entry { \
|
||||
__req_t req; \
|
||||
__rsp_t rsp; \
|
||||
}; \
|
||||
\
|
||||
/* Shared ring page */ \
|
||||
struct __name##_sring { \
|
||||
RING_IDX req_prod, req_event; \
|
||||
RING_IDX rsp_prod, rsp_event; \
|
||||
union { \
|
||||
struct { \
|
||||
u8 smartpoll_active; \
|
||||
} netif; \
|
||||
struct { \
|
||||
u8 msg; \
|
||||
} tapif_user; \
|
||||
u8 pvt_pad[4]; \
|
||||
} pvt; \
|
||||
u8 __pad[44]; \
|
||||
union __name##_sring_entry ring[1]; /* variable-length */ \
|
||||
}; \
|
||||
\
|
||||
/* "Front" end's private variables */ \
|
||||
struct __name##_front_ring { \
|
||||
RING_IDX req_prod_pvt; \
|
||||
RING_IDX rsp_cons; \
|
||||
unsigned int nr_ents; \
|
||||
struct __name##_sring *sring; \
|
||||
}; \
|
||||
\
|
||||
/* "Back" end's private variables */ \
|
||||
struct __name##_back_ring { \
|
||||
RING_IDX rsp_prod_pvt; \
|
||||
RING_IDX req_cons; \
|
||||
unsigned int nr_ents; \
|
||||
struct __name##_sring *sring; \
|
||||
}; \
|
||||
\
|
||||
/* Syntactic sugar */ \
|
||||
typedef struct __name##_sring __name##_sring_t; \
|
||||
typedef struct __name##_front_ring __name##_front_ring_t; \
|
||||
typedef struct __name##_back_ring __name##_back_ring_t
|
||||
|
||||
/*
|
||||
* Macros for manipulating rings.
|
||||
*
|
||||
* FRONT_RING_whatever works on the "front end" of a ring: here
|
||||
* requests are pushed on to the ring and responses taken off it.
|
||||
*
|
||||
* BACK_RING_whatever works on the "back end" of a ring: here
|
||||
* requests are taken off the ring and responses put on.
|
||||
*
|
||||
* N.B. these macros do NO INTERLOCKS OR FLOW CONTROL.
|
||||
* This is OK in 1-for-1 request-response situations where the
|
||||
* requestor (front end) never has more than RING_SIZE()-1
|
||||
* outstanding requests.
|
||||
*/
|
||||
|
||||
/* Initialising empty rings */
|
||||
#define SHARED_RING_INIT(_s) do { \
|
||||
(_s)->req_prod = (_s)->rsp_prod = 0; \
|
||||
(_s)->req_event = (_s)->rsp_event = 1; \
|
||||
(void)memset((_s)->pvt.pvt_pad, 0, sizeof((_s)->pvt.pvt_pad)); \
|
||||
(void)memset((_s)->__pad, 0, sizeof((_s)->__pad)); \
|
||||
} while (0)
|
||||
|
||||
#define FRONT_RING_INIT(_r, _s, __size) do { \
|
||||
(_r)->req_prod_pvt = 0; \
|
||||
(_r)->rsp_cons = 0; \
|
||||
(_r)->nr_ents = __RING_SIZE(_s, __size); \
|
||||
(_r)->sring = (_s); \
|
||||
} while (0)
|
||||
|
||||
#define BACK_RING_INIT(_r, _s, __size) do { \
|
||||
(_r)->rsp_prod_pvt = 0; \
|
||||
(_r)->req_cons = 0; \
|
||||
(_r)->nr_ents = __RING_SIZE(_s, __size); \
|
||||
(_r)->sring = (_s); \
|
||||
} while (0)
|
||||
|
||||
/* How big is this ring? */
|
||||
#define RING_SIZE(_r) \
|
||||
((_r)->nr_ents)
|
||||
|
||||
/* Number of free requests (for use on front side only). */
|
||||
#define RING_FREE_REQUESTS(_r) \
|
||||
(RING_SIZE(_r) - ((_r)->req_prod_pvt - (_r)->rsp_cons))
|
||||
|
||||
/* Test if there is an empty slot available on the front ring.
|
||||
* (This is only meaningful from the front. )
|
||||
*/
|
||||
#define RING_FULL(_r) \
|
||||
(RING_FREE_REQUESTS(_r) == 0)
|
||||
|
||||
/* Test if there are outstanding messages to be processed on a ring. */
|
||||
#define RING_HAS_UNCONSUMED_RESPONSES(_r) \
|
||||
((_r)->sring->rsp_prod - (_r)->rsp_cons)
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define RING_HAS_UNCONSUMED_REQUESTS(_r) ({ \
|
||||
unsigned int req = (_r)->sring->req_prod - (_r)->req_cons; \
|
||||
unsigned int rsp = RING_SIZE(_r) - \
|
||||
((_r)->req_cons - (_r)->rsp_prod_pvt); \
|
||||
req < rsp ? req : rsp; \
|
||||
})
|
||||
#else
|
||||
/* Same as above, but without the nice GCC ({ ... }) syntax. */
|
||||
#define RING_HAS_UNCONSUMED_REQUESTS(_r) \
|
||||
((((_r)->sring->req_prod - (_r)->req_cons) < \
|
||||
(RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) ? \
|
||||
((_r)->sring->req_prod - (_r)->req_cons) : \
|
||||
(RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt)))
|
||||
#endif
|
||||
|
||||
/* Direct access to individual ring elements, by index. */
|
||||
#define RING_GET_REQUEST(_r, _idx) \
|
||||
(&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))
|
||||
|
||||
/*
|
||||
* Get a local copy of a request.
|
||||
*
|
||||
* Use this in preference to RING_GET_REQUEST() so all processing is
|
||||
* done on a local copy that cannot be modified by the other end.
|
||||
*
|
||||
* Note that https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145 may cause this
|
||||
* to be ineffective where _req is a struct which consists of only bitfields.
|
||||
*/
|
||||
#define RING_COPY_REQUEST(_r, _idx, _req) do { \
|
||||
/* Use volatile to force the copy into _req. */ \
|
||||
*(_req) = *(volatile typeof(_req))RING_GET_REQUEST(_r, _idx); \
|
||||
} while (0)
|
||||
|
||||
#define RING_GET_RESPONSE(_r, _idx) \
|
||||
(&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].rsp))
|
||||
|
||||
/* Loop termination condition: Would the specified index overflow the ring? */
|
||||
#define RING_REQUEST_CONS_OVERFLOW(_r, _cons) \
|
||||
(((_cons) - (_r)->rsp_prod_pvt) >= RING_SIZE(_r))
|
||||
|
||||
/* Ill-behaved frontend determination: Can there be this many requests? */
|
||||
#define RING_REQUEST_PROD_OVERFLOW(_r, _prod) \
|
||||
(((_prod) - (_r)->rsp_prod_pvt) > RING_SIZE(_r))
|
||||
|
||||
#define RING_PUSH_REQUESTS(_r) do { \
|
||||
xen_wmb(); /* back sees requests /before/ updated producer index */ \
|
||||
(_r)->sring->req_prod = (_r)->req_prod_pvt; \
|
||||
} while (0)
|
||||
|
||||
#define RING_PUSH_RESPONSES(_r) do { \
|
||||
xen_wmb(); /* front sees resps /before/ updated producer index */ \
|
||||
(_r)->sring->rsp_prod = (_r)->rsp_prod_pvt; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Notification hold-off (req_event and rsp_event):
|
||||
*
|
||||
* When queueing requests or responses on a shared ring, it may not always be
|
||||
* necessary to notify the remote end. For example, if requests are in flight
|
||||
* in a backend, the front may be able to queue further requests without
|
||||
* notifying the back (if the back checks for new requests when it queues
|
||||
* responses).
|
||||
*
|
||||
* When enqueuing requests or responses:
|
||||
*
|
||||
* Use RING_PUSH_{REQUESTS,RESPONSES}_AND_CHECK_NOTIFY(). The second argument
|
||||
* is a boolean return value. True indicates that the receiver requires an
|
||||
* asynchronous notification.
|
||||
*
|
||||
* After dequeuing requests or responses (before sleeping the connection):
|
||||
*
|
||||
* Use RING_FINAL_CHECK_FOR_REQUESTS() or RING_FINAL_CHECK_FOR_RESPONSES().
|
||||
* The second argument is a boolean return value. True indicates that there
|
||||
* are pending messages on the ring (i.e., the connection should not be put
|
||||
* to sleep).
|
||||
*
|
||||
* These macros will set the req_event/rsp_event field to trigger a
|
||||
* notification on the very next message that is enqueued. If you want to
|
||||
* create batches of work (i.e., only receive a notification after several
|
||||
* messages have been enqueued) then you will need to create a customised
|
||||
* version of the FINAL_CHECK macro in your own code, which sets the event
|
||||
* field appropriately.
|
||||
*/
|
||||
|
||||
#define RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(_r, _notify) do { \
|
||||
RING_IDX __old = (_r)->sring->req_prod; \
|
||||
RING_IDX __new = (_r)->req_prod_pvt; \
|
||||
xen_wmb(); /* back sees requests /before/ updated producer index */ \
|
||||
(_r)->sring->req_prod = __new; \
|
||||
xen_mb(); /* back sees new requests /before/ we check req_event */ \
|
||||
(_notify) = ((RING_IDX)(__new - (_r)->sring->req_event) < \
|
||||
(RING_IDX)(__new - __old)); \
|
||||
} while (0)
|
||||
|
||||
#define RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(_r, _notify) do { \
|
||||
RING_IDX __old = (_r)->sring->rsp_prod; \
|
||||
RING_IDX __new = (_r)->rsp_prod_pvt; \
|
||||
xen_wmb(); /* front sees resps /before/ updated producer index */ \
|
||||
(_r)->sring->rsp_prod = __new; \
|
||||
xen_mb(); /* front sees new resps /before/ we check rsp_event */ \
|
||||
(_notify) = ((RING_IDX)(__new - (_r)->sring->rsp_event) < \
|
||||
(RING_IDX)(__new - __old)); \
|
||||
} while (0)
|
||||
|
||||
#define RING_FINAL_CHECK_FOR_REQUESTS(_r, _work_to_do) do { \
|
||||
(_work_to_do) = RING_HAS_UNCONSUMED_REQUESTS(_r); \
|
||||
if (_work_to_do) \
|
||||
break; \
|
||||
(_r)->sring->req_event = (_r)->req_cons + 1; \
|
||||
xen_mb(); \
|
||||
(_work_to_do) = RING_HAS_UNCONSUMED_REQUESTS(_r); \
|
||||
} while (0)
|
||||
|
||||
#define RING_FINAL_CHECK_FOR_RESPONSES(_r, _work_to_do) do { \
|
||||
(_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r); \
|
||||
if (_work_to_do) \
|
||||
break; \
|
||||
(_r)->sring->rsp_event = (_r)->rsp_cons + 1; \
|
||||
xen_mb(); \
|
||||
(_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* DEFINE_XEN_FLEX_RING_AND_INTF defines two monodirectional rings and
|
||||
* functions to check if there is data on the ring, and to read and
|
||||
* write to them.
|
||||
*
|
||||
* DEFINE_XEN_FLEX_RING is similar to DEFINE_XEN_FLEX_RING_AND_INTF, but
|
||||
* does not define the indexes page. As different protocols can have
|
||||
* extensions to the basic format, this macro allow them to define their
|
||||
* own struct.
|
||||
*
|
||||
* XEN_FLEX_RING_SIZE
|
||||
* Convenience macro to calculate the size of one of the two rings
|
||||
* from the overall order.
|
||||
*
|
||||
* $NAME_mask
|
||||
* Function to apply the size mask to an index, to reduce the index
|
||||
* within the range [0-size].
|
||||
*
|
||||
* $NAME_read_packet
|
||||
* Function to read data from the ring. The amount of data to read is
|
||||
* specified by the "size" argument.
|
||||
*
|
||||
* $NAME_write_packet
|
||||
* Function to write data to the ring. The amount of data to write is
|
||||
* specified by the "size" argument.
|
||||
*
|
||||
* $NAME_get_ring_ptr
|
||||
* Convenience function that returns a pointer to read/write to the
|
||||
* ring at the right location.
|
||||
*
|
||||
* $NAME_data_intf
|
||||
* Indexes page, shared between frontend and backend. It also
|
||||
* contains the array of grant refs.
|
||||
*
|
||||
* $NAME_queued
|
||||
* Function to calculate how many bytes are currently on the ring,
|
||||
* ready to be read. It can also be used to calculate how much free
|
||||
* space is currently on the ring (XEN_FLEX_RING_SIZE() -
|
||||
* $NAME_queued()).
|
||||
*/
|
||||
|
||||
#ifndef XEN_PAGE_SHIFT
|
||||
/* The PAGE_SIZE for ring protocols and hypercall interfaces is always
|
||||
* 4K, regardless of the architecture, and page granularity chosen by
|
||||
* operating systems.
|
||||
*/
|
||||
#define XEN_PAGE_SHIFT 12
|
||||
#endif
|
||||
#define XEN_FLEX_RING_SIZE(order) \
|
||||
(1UL << ((order) + XEN_PAGE_SHIFT - 1))
|
||||
|
||||
#define DEFINE_XEN_FLEX_RING(name) \
|
||||
static inline RING_IDX name##_mask(RING_IDX idx, RING_IDX ring_size) \
|
||||
{ \
|
||||
return idx & (ring_size - 1); \
|
||||
} \
|
||||
\
|
||||
static inline unsigned char *name##_get_ring_ptr(unsigned char *buf, \
|
||||
RING_IDX idx, \
|
||||
RING_IDX ring_size) \
|
||||
{ \
|
||||
return buf + name##_mask(idx, ring_size); \
|
||||
} \
|
||||
\
|
||||
static inline void name##_read_packet(void *opaque, \
|
||||
const unsigned char *buf, \
|
||||
size_t size, \
|
||||
RING_IDX masked_prod, \
|
||||
RING_IDX *masked_cons, \
|
||||
RING_IDX ring_size) \
|
||||
{ \
|
||||
if (*masked_cons < masked_prod || \
|
||||
size <= ring_size - *masked_cons) { \
|
||||
memcpy(opaque, buf + *masked_cons, size); \
|
||||
} else { \
|
||||
memcpy(opaque, buf + *masked_cons, ring_size - *masked_cons); \
|
||||
memcpy((unsigned char *)opaque + ring_size - *masked_cons, buf, \
|
||||
size - (ring_size - *masked_cons)); \
|
||||
} \
|
||||
*masked_cons = name##_mask(*masked_cons + size, ring_size); \
|
||||
} \
|
||||
\
|
||||
static inline void name##_write_packet(unsigned char *buf, \
|
||||
const void *opaque, \
|
||||
size_t size, \
|
||||
RING_IDX *masked_prod, \
|
||||
RING_IDX masked_cons, \
|
||||
RING_IDX ring_size) \
|
||||
{ \
|
||||
if (*masked_prod < masked_cons || \
|
||||
size <= ring_size - *masked_prod) { \
|
||||
memcpy(buf + *masked_prod, opaque, size); \
|
||||
} else { \
|
||||
memcpy(buf + *masked_prod, opaque, ring_size - *masked_prod); \
|
||||
memcpy(buf, (unsigned char *)opaque + (ring_size - *masked_prod), \
|
||||
size - (ring_size - *masked_prod)); \
|
||||
} \
|
||||
*masked_prod = name##_mask(*masked_prod + size, ring_size); \
|
||||
} \
|
||||
\
|
||||
static inline RING_IDX name##_queued(RING_IDX prod, \
|
||||
RING_IDX cons, \
|
||||
RING_IDX ring_size) \
|
||||
{ \
|
||||
RING_IDX size; \
|
||||
\
|
||||
if (prod == cons) \
|
||||
return 0; \
|
||||
\
|
||||
prod = name##_mask(prod, ring_size); \
|
||||
cons = name##_mask(cons, ring_size); \
|
||||
\
|
||||
if (prod == cons) \
|
||||
return ring_size; \
|
||||
\
|
||||
if (prod > cons) \
|
||||
size = prod - cons; \
|
||||
else \
|
||||
size = ring_size - (cons - prod); \
|
||||
return size; \
|
||||
} \
|
||||
\
|
||||
struct name##_data { \
|
||||
unsigned char *in; /* half of the allocation */ \
|
||||
unsigned char *out; /* half of the allocation */ \
|
||||
}
|
||||
|
||||
#define DEFINE_XEN_FLEX_RING_AND_INTF(name) \
|
||||
struct name##_data_intf { \
|
||||
RING_IDX in_cons, in_prod; \
|
||||
\
|
||||
u8 pad1[56]; \
|
||||
\
|
||||
RING_IDX out_cons, out_prod; \
|
||||
\
|
||||
u8 pad2[56]; \
|
||||
\
|
||||
RING_IDX ring_order; \
|
||||
grant_ref_t ref[]; \
|
||||
}; \
|
||||
DEFINE_XEN_FLEX_RING(name)
|
||||
|
||||
#endif /* __XEN_PUBLIC_IO_RING_H__ */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
64
include/xen/interface/io/xenbus.h
Normal file
64
include/xen/interface/io/xenbus.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* xenbus.h
|
||||
*
|
||||
* Xenbus protocol details.
|
||||
*
|
||||
* Copyright (C) 2005 XenSource Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _XEN_PUBLIC_IO_XENBUS_H
|
||||
#define _XEN_PUBLIC_IO_XENBUS_H
|
||||
|
||||
/*
|
||||
* The state of either end of the Xenbus, i.e. the current communication
|
||||
* status of initialisation across the bus. States here imply nothing about
|
||||
* the state of the connection between the driver and the kernel's device
|
||||
* layers.
|
||||
*/
|
||||
enum xenbus_state {
|
||||
XenbusStateUnknown = 0,
|
||||
|
||||
XenbusStateInitialising = 1,
|
||||
|
||||
/*
|
||||
* InitWait: Finished early initialisation but waiting for information
|
||||
* from the peer or hotplug scripts.
|
||||
*/
|
||||
XenbusStateInitWait = 2,
|
||||
|
||||
/*
|
||||
* Initialised: Waiting for a connection from the peer.
|
||||
*/
|
||||
XenbusStateInitialised = 3,
|
||||
|
||||
XenbusStateConnected = 4,
|
||||
|
||||
/*
|
||||
* Closing: The device is being closed due to an error or an unplug event.
|
||||
*/
|
||||
XenbusStateClosing = 5,
|
||||
|
||||
XenbusStateClosed = 6,
|
||||
|
||||
/*
|
||||
* Reconfiguring: The device is being reconfigured.
|
||||
*/
|
||||
XenbusStateReconfiguring = 7,
|
||||
|
||||
XenbusStateReconfigured = 8
|
||||
};
|
||||
|
||||
typedef enum xenbus_state XenbusState;
|
||||
|
||||
#endif /* _XEN_PUBLIC_IO_XENBUS_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
134
include/xen/interface/io/xs_wire.h
Normal file
134
include/xen/interface/io/xs_wire.h
Normal file
@ -0,0 +1,134 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Details of the "wire" protocol between Xen Store Daemon and client
|
||||
* library or guest kernel.
|
||||
*
|
||||
* Copyright (C) 2005 Rusty Russell IBM Corporation
|
||||
*/
|
||||
|
||||
#ifndef _XS_WIRE_H
|
||||
#define _XS_WIRE_H
|
||||
|
||||
enum xsd_sockmsg_type {
|
||||
XS_CONTROL,
|
||||
#define XS_DEBUG XS_CONTROL
|
||||
XS_DIRECTORY,
|
||||
XS_READ,
|
||||
XS_GET_PERMS,
|
||||
XS_WATCH,
|
||||
XS_UNWATCH,
|
||||
XS_TRANSACTION_START,
|
||||
XS_TRANSACTION_END,
|
||||
XS_INTRODUCE,
|
||||
XS_RELEASE,
|
||||
XS_GET_DOMAIN_PATH,
|
||||
XS_WRITE,
|
||||
XS_MKDIR,
|
||||
XS_RM,
|
||||
XS_SET_PERMS,
|
||||
XS_WATCH_EVENT,
|
||||
XS_ERROR,
|
||||
XS_IS_DOMAIN_INTRODUCED,
|
||||
XS_RESUME,
|
||||
XS_SET_TARGET,
|
||||
/* XS_RESTRICT has been removed */
|
||||
XS_RESET_WATCHES = XS_SET_TARGET + 2,
|
||||
XS_DIRECTORY_PART,
|
||||
|
||||
XS_TYPE_COUNT, /* Number of valid types. */
|
||||
|
||||
XS_INVALID = 0xffff /* Guaranteed to remain an invalid type */
|
||||
};
|
||||
|
||||
#define XS_WRITE_NONE "NONE"
|
||||
#define XS_WRITE_CREATE "CREATE"
|
||||
#define XS_WRITE_CREATE_EXCL "CREATE|EXCL"
|
||||
|
||||
/* We hand errors as strings, for portability. */
|
||||
struct xsd_errors {
|
||||
int errnum;
|
||||
const char *errstring;
|
||||
};
|
||||
|
||||
#ifdef EINVAL
|
||||
#define XSD_ERROR(x) { x, #x }
|
||||
/* LINTED: static unused */
|
||||
static struct xsd_errors xsd_errors[]
|
||||
#if defined(__GNUC__)
|
||||
__attribute__((unused))
|
||||
#endif
|
||||
= {
|
||||
XSD_ERROR(EINVAL),
|
||||
XSD_ERROR(EACCES),
|
||||
XSD_ERROR(EEXIST),
|
||||
XSD_ERROR(EISDIR),
|
||||
XSD_ERROR(ENOENT),
|
||||
XSD_ERROR(ENOMEM),
|
||||
XSD_ERROR(ENOSPC),
|
||||
XSD_ERROR(EIO),
|
||||
XSD_ERROR(ENOTEMPTY),
|
||||
XSD_ERROR(ENOSYS),
|
||||
XSD_ERROR(EROFS),
|
||||
XSD_ERROR(EBUSY),
|
||||
XSD_ERROR(EAGAIN),
|
||||
XSD_ERROR(EISCONN),
|
||||
XSD_ERROR(E2BIG)
|
||||
};
|
||||
#endif
|
||||
|
||||
struct xsd_sockmsg {
|
||||
u32 type; /* XS_??? */
|
||||
u32 req_id;/* Request identifier, echoed in daemon's response. */
|
||||
u32 tx_id; /* Transaction id (0 if not related to a transaction). */
|
||||
u32 len; /* Length of data following this. */
|
||||
|
||||
/* Generally followed by nul-terminated string(s). */
|
||||
};
|
||||
|
||||
enum xs_watch_type {
|
||||
XS_WATCH_PATH = 0,
|
||||
XS_WATCH_TOKEN
|
||||
};
|
||||
|
||||
/*
|
||||
* `incontents 150 xenstore_struct XenStore wire protocol.
|
||||
*
|
||||
* Inter-domain shared memory communications.
|
||||
*/
|
||||
#define XENSTORE_RING_SIZE 1024
|
||||
typedef u32 XENSTORE_RING_IDX;
|
||||
#define MASK_XENSTORE_IDX(idx) ((idx) & (XENSTORE_RING_SIZE - 1))
|
||||
struct xenstore_domain_interface {
|
||||
char req[XENSTORE_RING_SIZE]; /* Requests to xenstore daemon. */
|
||||
char rsp[XENSTORE_RING_SIZE]; /* Replies and async watch events. */
|
||||
XENSTORE_RING_IDX req_cons, req_prod;
|
||||
XENSTORE_RING_IDX rsp_cons, rsp_prod;
|
||||
u32 server_features; /* Bitmap of features supported by the server */
|
||||
u32 connection;
|
||||
};
|
||||
|
||||
/* Violating this is very bad. See docs/misc/xenstore.txt. */
|
||||
#define XENSTORE_PAYLOAD_MAX 4096
|
||||
|
||||
/* Violating these just gets you an error back */
|
||||
#define XENSTORE_ABS_PATH_MAX 3072
|
||||
#define XENSTORE_REL_PATH_MAX 2048
|
||||
|
||||
/* The ability to reconnect a ring */
|
||||
#define XENSTORE_SERVER_FEATURE_RECONNECTION 1
|
||||
|
||||
/* Valid values for the connection field */
|
||||
#define XENSTORE_CONNECTED 0 /* the steady-state */
|
||||
#define XENSTORE_RECONNECT 1 /* guest has initiated a reconnect */
|
||||
|
||||
#endif /* _XS_WIRE_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
332
include/xen/interface/memory.h
Normal file
332
include/xen/interface/memory.h
Normal file
@ -0,0 +1,332 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* memory.h
|
||||
*
|
||||
* Memory reservation and information.
|
||||
*
|
||||
* Copyright (c) 2005, Keir Fraser <keir@xensource.com>
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PUBLIC_MEMORY_H__
|
||||
#define __XEN_PUBLIC_MEMORY_H__
|
||||
|
||||
/*
|
||||
* Increase or decrease the specified domain's memory reservation. Returns a
|
||||
* -ve errcode on failure, or the # extents successfully allocated or freed.
|
||||
* arg == addr of struct xen_memory_reservation.
|
||||
*/
|
||||
#define XENMEM_increase_reservation 0
|
||||
#define XENMEM_decrease_reservation 1
|
||||
#define XENMEM_populate_physmap 6
|
||||
struct xen_memory_reservation {
|
||||
/*
|
||||
* XENMEM_increase_reservation:
|
||||
* OUT: MFN (*not* GMFN) bases of extents that were allocated
|
||||
* XENMEM_decrease_reservation:
|
||||
* IN: GMFN bases of extents to free
|
||||
* XENMEM_populate_physmap:
|
||||
* IN: GPFN bases of extents to populate with memory
|
||||
* OUT: GMFN bases of extents that were allocated
|
||||
* (NB. This command also updates the mach_to_phys translation table)
|
||||
*/
|
||||
GUEST_HANDLE(xen_pfn_t)extent_start;
|
||||
|
||||
/* Number of extents, and size/alignment of each (2^extent_order pages). */
|
||||
xen_ulong_t nr_extents;
|
||||
unsigned int extent_order;
|
||||
|
||||
/*
|
||||
* Maximum # bits addressable by the user of the allocated region (e.g.,
|
||||
* I/O devices often have a 32-bit limitation even in 64-bit systems). If
|
||||
* zero then the user has no addressing restriction.
|
||||
* This field is not used by XENMEM_decrease_reservation.
|
||||
*/
|
||||
unsigned int address_bits;
|
||||
|
||||
/*
|
||||
* Domain whose reservation is being changed.
|
||||
* Unprivileged domains can specify only DOMID_SELF.
|
||||
*/
|
||||
domid_t domid;
|
||||
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(xen_memory_reservation);
|
||||
|
||||
/*
|
||||
* An atomic exchange of memory pages. If return code is zero then
|
||||
* @out.extent_list provides GMFNs of the newly-allocated memory.
|
||||
* Returns zero on complete success, otherwise a negative error code.
|
||||
* On complete success then always @nr_exchanged == @in.nr_extents.
|
||||
* On partial success @nr_exchanged indicates how much work was done.
|
||||
*/
|
||||
#define XENMEM_exchange 11
|
||||
struct xen_memory_exchange {
|
||||
/*
|
||||
* [IN] Details of memory extents to be exchanged (GMFN bases).
|
||||
* Note that @in.address_bits is ignored and unused.
|
||||
*/
|
||||
struct xen_memory_reservation in;
|
||||
|
||||
/*
|
||||
* [IN/OUT] Details of new memory extents.
|
||||
* We require that:
|
||||
* 1. @in.domid == @out.domid
|
||||
* 2. @in.nr_extents << @in.extent_order ==
|
||||
* @out.nr_extents << @out.extent_order
|
||||
* 3. @in.extent_start and @out.extent_start lists must not overlap
|
||||
* 4. @out.extent_start lists GPFN bases to be populated
|
||||
* 5. @out.extent_start is overwritten with allocated GMFN bases
|
||||
*/
|
||||
struct xen_memory_reservation out;
|
||||
|
||||
/*
|
||||
* [OUT] Number of input extents that were successfully exchanged:
|
||||
* 1. The first @nr_exchanged input extents were successfully
|
||||
* deallocated.
|
||||
* 2. The corresponding first entries in the output extent list correctly
|
||||
* indicate the GMFNs that were successfully exchanged.
|
||||
* 3. All other input and output extents are untouched.
|
||||
* 4. If not all input exents are exchanged then the return code of this
|
||||
* command will be non-zero.
|
||||
* 5. THIS FIELD MUST BE INITIALISED TO ZERO BY THE CALLER!
|
||||
*/
|
||||
xen_ulong_t nr_exchanged;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(xen_memory_exchange);
|
||||
/*
|
||||
* Returns the maximum machine frame number of mapped RAM in this system.
|
||||
* This command always succeeds (it never returns an error code).
|
||||
* arg == NULL.
|
||||
*/
|
||||
#define XENMEM_maximum_ram_page 2
|
||||
|
||||
/*
|
||||
* Returns the current or maximum memory reservation, in pages, of the
|
||||
* specified domain (may be DOMID_SELF). Returns -ve errcode on failure.
|
||||
* arg == addr of domid_t.
|
||||
*/
|
||||
#define XENMEM_current_reservation 3
|
||||
#define XENMEM_maximum_reservation 4
|
||||
|
||||
/*
|
||||
* Returns a list of MFN bases of 2MB extents comprising the machine_to_phys
|
||||
* mapping table. Architectures which do not have a m2p table do not implement
|
||||
* this command.
|
||||
* arg == addr of xen_machphys_mfn_list_t.
|
||||
*/
|
||||
#define XENMEM_machphys_mfn_list 5
|
||||
struct xen_machphys_mfn_list {
|
||||
/*
|
||||
* Size of the 'extent_start' array. Fewer entries will be filled if the
|
||||
* machphys table is smaller than max_extents * 2MB.
|
||||
*/
|
||||
unsigned int max_extents;
|
||||
|
||||
/*
|
||||
* Pointer to buffer to fill with list of extent starts. If there are
|
||||
* any large discontiguities in the machine address space, 2MB gaps in
|
||||
* the machphys table will be represented by an MFN base of zero.
|
||||
*/
|
||||
GUEST_HANDLE(xen_pfn_t)extent_start;
|
||||
|
||||
/*
|
||||
* Number of extents written to the above array. This will be smaller
|
||||
* than 'max_extents' if the machphys table is smaller than max_e * 2MB.
|
||||
*/
|
||||
unsigned int nr_extents;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(xen_machphys_mfn_list);
|
||||
|
||||
/*
|
||||
* Returns the location in virtual address space of the machine_to_phys
|
||||
* mapping table. Architectures which do not have a m2p table, or which do not
|
||||
* map it by default into guest address space, do not implement this command.
|
||||
* arg == addr of xen_machphys_mapping_t.
|
||||
*/
|
||||
#define XENMEM_machphys_mapping 12
|
||||
struct xen_machphys_mapping {
|
||||
xen_ulong_t v_start, v_end; /* Start and end virtual addresses. */
|
||||
xen_ulong_t max_mfn; /* Maximum MFN that can be looked up. */
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(xen_machphys_mapping_t);
|
||||
|
||||
#define XENMAPSPACE_shared_info 0 /* shared info page */
|
||||
#define XENMAPSPACE_grant_table 1 /* grant table page */
|
||||
#define XENMAPSPACE_gmfn 2 /* GMFN */
|
||||
#define XENMAPSPACE_gmfn_range 3 /* GMFN range, XENMEM_add_to_physmap only. */
|
||||
#define XENMAPSPACE_gmfn_foreign 4 /* GMFN from another dom,
|
||||
* XENMEM_add_to_physmap_range only.
|
||||
*/
|
||||
#define XENMAPSPACE_dev_mmio 5 /* device mmio region */
|
||||
|
||||
/*
|
||||
* Sets the GPFN at which a particular page appears in the specified guest's
|
||||
* pseudophysical address space.
|
||||
* arg == addr of xen_add_to_physmap_t.
|
||||
*/
|
||||
#define XENMEM_add_to_physmap 7
|
||||
struct xen_add_to_physmap {
|
||||
/* Which domain to change the mapping for. */
|
||||
domid_t domid;
|
||||
|
||||
/* Number of pages to go through for gmfn_range */
|
||||
u16 size;
|
||||
|
||||
/* Source mapping space. */
|
||||
unsigned int space;
|
||||
|
||||
/* Index into source mapping space. */
|
||||
xen_ulong_t idx;
|
||||
|
||||
/* GPFN where the source mapping page should appear. */
|
||||
xen_pfn_t gpfn;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(xen_add_to_physmap);
|
||||
|
||||
/*** REMOVED ***/
|
||||
/*#define XENMEM_translate_gpfn_list 8*/
|
||||
|
||||
#define XENMEM_add_to_physmap_range 23
|
||||
struct xen_add_to_physmap_range {
|
||||
/* IN */
|
||||
/* Which domain to change the mapping for. */
|
||||
domid_t domid;
|
||||
u16 space; /* => enum phys_map_space */
|
||||
|
||||
/* Number of pages to go through */
|
||||
u16 size;
|
||||
domid_t foreign_domid; /* IFF gmfn_foreign */
|
||||
|
||||
/* Indexes into space being mapped. */
|
||||
GUEST_HANDLE(xen_ulong_t)idxs;
|
||||
|
||||
/* GPFN in domid where the source mapping page should appear. */
|
||||
GUEST_HANDLE(xen_pfn_t)gpfns;
|
||||
|
||||
/* OUT */
|
||||
|
||||
/* Per index error code. */
|
||||
GUEST_HANDLE(int)errs;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(xen_add_to_physmap_range);
|
||||
|
||||
/*
|
||||
* Returns the pseudo-physical memory map as it was when the domain
|
||||
* was started (specified by XENMEM_set_memory_map).
|
||||
* arg == addr of struct xen_memory_map.
|
||||
*/
|
||||
#define XENMEM_memory_map 9
|
||||
struct xen_memory_map {
|
||||
/*
|
||||
* On call the number of entries which can be stored in buffer. On
|
||||
* return the number of entries which have been stored in
|
||||
* buffer.
|
||||
*/
|
||||
unsigned int nr_entries;
|
||||
|
||||
/*
|
||||
* Entries in the buffer are in the same format as returned by the
|
||||
* BIOS INT 0x15 EAX=0xE820 call.
|
||||
*/
|
||||
GUEST_HANDLE(void)buffer;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(xen_memory_map);
|
||||
|
||||
/*
|
||||
* Returns the real physical memory map. Passes the same structure as
|
||||
* XENMEM_memory_map.
|
||||
* arg == addr of struct xen_memory_map.
|
||||
*/
|
||||
#define XENMEM_machine_memory_map 10
|
||||
|
||||
/*
|
||||
* Unmaps the page appearing at a particular GPFN from the specified guest's
|
||||
* pseudophysical address space.
|
||||
* arg == addr of xen_remove_from_physmap_t.
|
||||
*/
|
||||
#define XENMEM_remove_from_physmap 15
|
||||
struct xen_remove_from_physmap {
|
||||
/* Which domain to change the mapping for. */
|
||||
domid_t domid;
|
||||
|
||||
/* GPFN of the current mapping of the page. */
|
||||
xen_pfn_t gpfn;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(xen_remove_from_physmap);
|
||||
|
||||
/*
|
||||
* Get the pages for a particular guest resource, so that they can be
|
||||
* mapped directly by a tools domain.
|
||||
*/
|
||||
#define XENMEM_acquire_resource 28
|
||||
struct xen_mem_acquire_resource {
|
||||
/* IN - The domain whose resource is to be mapped */
|
||||
domid_t domid;
|
||||
/* IN - the type of resource */
|
||||
u16 type;
|
||||
|
||||
#define XENMEM_resource_ioreq_server 0
|
||||
#define XENMEM_resource_grant_table 1
|
||||
|
||||
/*
|
||||
* IN - a type-specific resource identifier, which must be zero
|
||||
* unless stated otherwise.
|
||||
*
|
||||
* type == XENMEM_resource_ioreq_server -> id == ioreq server id
|
||||
* type == XENMEM_resource_grant_table -> id defined below
|
||||
*/
|
||||
u32 id;
|
||||
|
||||
#define XENMEM_resource_grant_table_id_shared 0
|
||||
#define XENMEM_resource_grant_table_id_status 1
|
||||
|
||||
/* IN/OUT - As an IN parameter number of frames of the resource
|
||||
* to be mapped. However, if the specified value is 0 and
|
||||
* frame_list is NULL then this field will be set to the
|
||||
* maximum value supported by the implementation on return.
|
||||
*/
|
||||
u32 nr_frames;
|
||||
/*
|
||||
* OUT - Must be zero on entry. On return this may contain a bitwise
|
||||
* OR of the following values.
|
||||
*/
|
||||
u32 flags;
|
||||
|
||||
/* The resource pages have been assigned to the calling domain */
|
||||
#define _XENMEM_rsrc_acq_caller_owned 0
|
||||
#define XENMEM_rsrc_acq_caller_owned (1u << _XENMEM_rsrc_acq_caller_owned)
|
||||
|
||||
/*
|
||||
* IN - the index of the initial frame to be mapped. This parameter
|
||||
* is ignored if nr_frames is 0.
|
||||
*/
|
||||
u64 frame;
|
||||
|
||||
#define XENMEM_resource_ioreq_server_frame_bufioreq 0
|
||||
#define XENMEM_resource_ioreq_server_frame_ioreq(n) (1 + (n))
|
||||
|
||||
/*
|
||||
* IN/OUT - If the tools domain is PV then, upon return, frame_list
|
||||
* will be populated with the MFNs of the resource.
|
||||
* If the tools domain is HVM then it is expected that, on
|
||||
* entry, frame_list will be populated with a list of GFNs
|
||||
* that will be mapped to the MFNs of the resource.
|
||||
* If -EIO is returned then the frame_list has only been
|
||||
* partially mapped and it is up to the caller to unmap all
|
||||
* the GFNs.
|
||||
* This parameter may be NULL if nr_frames is 0.
|
||||
*/
|
||||
GUEST_HANDLE(xen_pfn_t)frame_list;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(xen_mem_acquire_resource);
|
||||
|
||||
#endif /* __XEN_PUBLIC_MEMORY_H__ */
|
171
include/xen/interface/sched.h
Normal file
171
include/xen/interface/sched.h
Normal file
@ -0,0 +1,171 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* sched.h
|
||||
*
|
||||
* Scheduler state interactions
|
||||
*
|
||||
* Copyright (c) 2005, Keir Fraser <keir@xensource.com>
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PUBLIC_SCHED_H__
|
||||
#define __XEN_PUBLIC_SCHED_H__
|
||||
|
||||
#include <xen/interface/event_channel.h>
|
||||
|
||||
/*
|
||||
* Guest Scheduler Operations
|
||||
*
|
||||
* The SCHEDOP interface provides mechanisms for a guest to interact
|
||||
* with the scheduler, including yield, blocking and shutting itself
|
||||
* down.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The prototype for this hypercall is:
|
||||
* long HYPERVISOR_sched_op(enum sched_op cmd, void *arg, ...)
|
||||
*
|
||||
* @cmd == SCHEDOP_??? (scheduler operation).
|
||||
* @arg == Operation-specific extra argument(s), as described below.
|
||||
* ... == Additional Operation-specific extra arguments, described below.
|
||||
*
|
||||
* Versions of Xen prior to 3.0.2 provided only the following legacy version
|
||||
* of this hypercall, supporting only the commands yield, block and shutdown:
|
||||
* long sched_op(int cmd, unsigned long arg)
|
||||
* @cmd == SCHEDOP_??? (scheduler operation).
|
||||
* @arg == 0 (SCHEDOP_yield and SCHEDOP_block)
|
||||
* == SHUTDOWN_* code (SCHEDOP_shutdown)
|
||||
*
|
||||
* This legacy version is available to new guests as:
|
||||
* long HYPERVISOR_sched_op_compat(enum sched_op cmd, unsigned long arg)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Voluntarily yield the CPU.
|
||||
* @arg == NULL.
|
||||
*/
|
||||
#define SCHEDOP_yield 0
|
||||
|
||||
/*
|
||||
* Block execution of this VCPU until an event is received for processing.
|
||||
* If called with event upcalls masked, this operation will atomically
|
||||
* reenable event delivery and check for pending events before blocking the
|
||||
* VCPU. This avoids a "wakeup waiting" race.
|
||||
* @arg == NULL.
|
||||
*/
|
||||
#define SCHEDOP_block 1
|
||||
|
||||
/*
|
||||
* Halt execution of this domain (all VCPUs) and notify the system controller.
|
||||
* @arg == pointer to sched_shutdown structure.
|
||||
*
|
||||
* If the sched_shutdown_t reason is SHUTDOWN_suspend then
|
||||
* x86 PV guests must also set RDX (EDX for 32-bit guests) to the MFN
|
||||
* of the guest's start info page. RDX/EDX is the third hypercall
|
||||
* argument.
|
||||
*
|
||||
* In addition, which reason is SHUTDOWN_suspend this hypercall
|
||||
* returns 1 if suspend was cancelled or the domain was merely
|
||||
* checkpointed, and 0 if it is resuming in a new domain.
|
||||
*/
|
||||
#define SCHEDOP_shutdown 2
|
||||
|
||||
/*
|
||||
* Poll a set of event-channel ports. Return when one or more are pending. An
|
||||
* optional timeout may be specified.
|
||||
* @arg == pointer to sched_poll structure.
|
||||
*/
|
||||
#define SCHEDOP_poll 3
|
||||
|
||||
/*
|
||||
* Declare a shutdown for another domain. The main use of this function is
|
||||
* in interpreting shutdown requests and reasons for fully-virtualized
|
||||
* domains. A para-virtualized domain may use SCHEDOP_shutdown directly.
|
||||
* @arg == pointer to sched_remote_shutdown structure.
|
||||
*/
|
||||
#define SCHEDOP_remote_shutdown 4
|
||||
|
||||
/*
|
||||
* Latch a shutdown code, so that when the domain later shuts down it
|
||||
* reports this code to the control tools.
|
||||
* @arg == sched_shutdown, as for SCHEDOP_shutdown.
|
||||
*/
|
||||
#define SCHEDOP_shutdown_code 5
|
||||
|
||||
/*
|
||||
* Setup, poke and destroy a domain watchdog timer.
|
||||
* @arg == pointer to sched_watchdog structure.
|
||||
* With id == 0, setup a domain watchdog timer to cause domain shutdown
|
||||
* after timeout, returns watchdog id.
|
||||
* With id != 0 and timeout == 0, destroy domain watchdog timer.
|
||||
* With id != 0 and timeout != 0, poke watchdog timer and set new timeout.
|
||||
*/
|
||||
#define SCHEDOP_watchdog 6
|
||||
|
||||
/*
|
||||
* Override the current vcpu affinity by pinning it to one physical cpu or
|
||||
* undo this override restoring the previous affinity.
|
||||
* @arg == pointer to sched_pin_override structure.
|
||||
*
|
||||
* A negative pcpu value will undo a previous pin override and restore the
|
||||
* previous cpu affinity.
|
||||
* This call is allowed for the hardware domain only and requires the cpu
|
||||
* to be part of the domain's cpupool.
|
||||
*/
|
||||
#define SCHEDOP_pin_override 7
|
||||
|
||||
struct sched_shutdown {
|
||||
unsigned int reason; /* SHUTDOWN_* => shutdown reason */
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(sched_shutdown);
|
||||
|
||||
struct sched_poll {
|
||||
GUEST_HANDLE(evtchn_port_t)ports;
|
||||
unsigned int nr_ports;
|
||||
u64 timeout;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(sched_poll);
|
||||
|
||||
struct sched_remote_shutdown {
|
||||
domid_t domain_id; /* Remote domain ID */
|
||||
unsigned int reason; /* SHUTDOWN_* => shutdown reason */
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(sched_remote_shutdown);
|
||||
|
||||
struct sched_watchdog {
|
||||
u32 id; /* watchdog ID */
|
||||
u32 timeout; /* timeout */
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(sched_watchdog);
|
||||
|
||||
struct sched_pin_override {
|
||||
s32 pcpu;
|
||||
};
|
||||
|
||||
DEFINE_GUEST_HANDLE_STRUCT(sched_pin_override);
|
||||
|
||||
/*
|
||||
* Reason codes for SCHEDOP_shutdown. These may be interpreted by control
|
||||
* software to determine the appropriate action. For the most part, Xen does
|
||||
* not care about the shutdown code.
|
||||
*/
|
||||
#define SHUTDOWN_poweroff 0 /* Domain exited normally. Clean up and kill. */
|
||||
#define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */
|
||||
#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */
|
||||
#define SHUTDOWN_crash 3 /* Tell controller we've crashed. */
|
||||
#define SHUTDOWN_watchdog 4 /* Restart because watchdog time expired. */
|
||||
|
||||
/*
|
||||
* Domain asked to perform 'soft reset' for it. The expected behavior is to
|
||||
* reset internal Xen state for the domain returning it to the point where it
|
||||
* was created but leaving the domain's memory contents and vCPU contexts
|
||||
* intact. This will allow the domain to start over and set up all Xen specific
|
||||
* interfaces again.
|
||||
*/
|
||||
#define SHUTDOWN_soft_reset 5
|
||||
#define SHUTDOWN_MAX 5 /* Maximum valid shutdown reason. */
|
||||
|
||||
#endif /* __XEN_PUBLIC_SCHED_H__ */
|
208
include/xen/interface/xen.h
Normal file
208
include/xen/interface/xen.h
Normal file
@ -0,0 +1,208 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* xen.h
|
||||
*
|
||||
* Guest OS interface to Xen.
|
||||
*
|
||||
* Copyright (c) 2004, K A Fraser
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PUBLIC_XEN_H__
|
||||
#define __XEN_PUBLIC_XEN_H__
|
||||
|
||||
#include <xen/arm/interface.h>
|
||||
|
||||
/*
|
||||
* XEN "SYSTEM CALLS" (a.k.a. HYPERCALLS).
|
||||
*/
|
||||
|
||||
/*
|
||||
* x86_32: EAX = vector; EBX, ECX, EDX, ESI, EDI = args 1, 2, 3, 4, 5.
|
||||
* EAX = return value
|
||||
* (argument registers may be clobbered on return)
|
||||
* x86_64: RAX = vector; RDI, RSI, RDX, R10, R8, R9 = args 1, 2, 3, 4, 5, 6.
|
||||
* RAX = return value
|
||||
* (argument registers not clobbered on return; RCX, R11 are)
|
||||
*/
|
||||
#define __HYPERVISOR_set_trap_table 0
|
||||
#define __HYPERVISOR_mmu_update 1
|
||||
#define __HYPERVISOR_set_gdt 2
|
||||
#define __HYPERVISOR_stack_switch 3
|
||||
#define __HYPERVISOR_set_callbacks 4
|
||||
#define __HYPERVISOR_fpu_taskswitch 5
|
||||
#define __HYPERVISOR_sched_op_compat 6
|
||||
#define __HYPERVISOR_platform_op 7
|
||||
#define __HYPERVISOR_set_debugreg 8
|
||||
#define __HYPERVISOR_get_debugreg 9
|
||||
#define __HYPERVISOR_update_descriptor 10
|
||||
#define __HYPERVISOR_memory_op 12
|
||||
#define __HYPERVISOR_multicall 13
|
||||
#define __HYPERVISOR_update_va_mapping 14
|
||||
#define __HYPERVISOR_set_timer_op 15
|
||||
#define __HYPERVISOR_event_channel_op_compat 16
|
||||
#define __HYPERVISOR_xen_version 17
|
||||
#define __HYPERVISOR_console_io 18
|
||||
#define __HYPERVISOR_physdev_op_compat 19
|
||||
#define __HYPERVISOR_grant_table_op 20
|
||||
#define __HYPERVISOR_vm_assist 21
|
||||
#define __HYPERVISOR_update_va_mapping_otherdomain 22
|
||||
#define __HYPERVISOR_iret 23 /* x86 only */
|
||||
#define __HYPERVISOR_vcpu_op 24
|
||||
#define __HYPERVISOR_set_segment_base 25 /* x86/64 only */
|
||||
#define __HYPERVISOR_mmuext_op 26
|
||||
#define __HYPERVISOR_xsm_op 27
|
||||
#define __HYPERVISOR_nmi_op 28
|
||||
#define __HYPERVISOR_sched_op 29
|
||||
#define __HYPERVISOR_callback_op 30
|
||||
#define __HYPERVISOR_xenoprof_op 31
|
||||
#define __HYPERVISOR_event_channel_op 32
|
||||
#define __HYPERVISOR_physdev_op 33
|
||||
#define __HYPERVISOR_hvm_op 34
|
||||
#define __HYPERVISOR_sysctl 35
|
||||
#define __HYPERVISOR_domctl 36
|
||||
#define __HYPERVISOR_kexec_op 37
|
||||
#define __HYPERVISOR_tmem_op 38
|
||||
#define __HYPERVISOR_xc_reserved_op 39 /* reserved for XenClient */
|
||||
#define __HYPERVISOR_xenpmu_op 40
|
||||
#define __HYPERVISOR_dm_op 41
|
||||
|
||||
/* Architecture-specific hypercall definitions. */
|
||||
#define __HYPERVISOR_arch_0 48
|
||||
#define __HYPERVISOR_arch_1 49
|
||||
#define __HYPERVISOR_arch_2 50
|
||||
#define __HYPERVISOR_arch_3 51
|
||||
#define __HYPERVISOR_arch_4 52
|
||||
#define __HYPERVISOR_arch_5 53
|
||||
#define __HYPERVISOR_arch_6 54
|
||||
#define __HYPERVISOR_arch_7 55
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
typedef u16 domid_t;
|
||||
|
||||
/* Domain ids >= DOMID_FIRST_RESERVED cannot be used for ordinary domains. */
|
||||
#define DOMID_FIRST_RESERVED (0x7FF0U)
|
||||
|
||||
/* DOMID_SELF is used in certain contexts to refer to oneself. */
|
||||
#define DOMID_SELF (0x7FF0U)
|
||||
|
||||
/*
|
||||
* DOMID_IO is used to restrict page-table updates to mapping I/O memory.
|
||||
* Although no Foreign Domain need be specified to map I/O pages, DOMID_IO
|
||||
* is useful to ensure that no mappings to the OS's own heap are accidentally
|
||||
* installed. (e.g., in Linux this could cause havoc as reference counts
|
||||
* aren't adjusted on the I/O-mapping code path).
|
||||
* This only makes sense in MMUEXT_SET_FOREIGNDOM, but in that context can
|
||||
* be specified by any calling domain.
|
||||
*/
|
||||
#define DOMID_IO (0x7FF1U)
|
||||
|
||||
/*
|
||||
* DOMID_XEN is used to allow privileged domains to map restricted parts of
|
||||
* Xen's heap space (e.g., the machine_to_phys table).
|
||||
* This only makes sense in MMUEXT_SET_FOREIGNDOM, and is only permitted if
|
||||
* the caller is privileged.
|
||||
*/
|
||||
#define DOMID_XEN (0x7FF2U)
|
||||
|
||||
/* DOMID_COW is used as the owner of sharable pages */
|
||||
#define DOMID_COW (0x7FF3U)
|
||||
|
||||
/* DOMID_INVALID is used to identify pages with unknown owner. */
|
||||
#define DOMID_INVALID (0x7FF4U)
|
||||
|
||||
/* Idle domain. */
|
||||
#define DOMID_IDLE (0x7FFFU)
|
||||
|
||||
struct vcpu_info {
|
||||
/*
|
||||
* 'evtchn_upcall_pending' is written non-zero by Xen to indicate
|
||||
* a pending notification for a particular VCPU. It is then cleared
|
||||
* by the guest OS /before/ checking for pending work, thus avoiding
|
||||
* a set-and-check race. Note that the mask is only accessed by Xen
|
||||
* on the CPU that is currently hosting the VCPU. This means that the
|
||||
* pending and mask flags can be updated by the guest without special
|
||||
* synchronisation (i.e., no need for the x86 LOCK prefix).
|
||||
* This may seem suboptimal because if the pending flag is set by
|
||||
* a different CPU then an IPI may be scheduled even when the mask
|
||||
* is set. However, note:
|
||||
* 1. The task of 'interrupt holdoff' is covered by the per-event-
|
||||
* channel mask bits. A 'noisy' event that is continually being
|
||||
* triggered can be masked at source at this very precise
|
||||
* granularity.
|
||||
* 2. The main purpose of the per-VCPU mask is therefore to restrict
|
||||
* reentrant execution: whether for concurrency control, or to
|
||||
* prevent unbounded stack usage. Whatever the purpose, we expect
|
||||
* that the mask will be asserted only for short periods at a time,
|
||||
* and so the likelihood of a 'spurious' IPI is suitably small.
|
||||
* The mask is read before making an event upcall to the guest: a
|
||||
* non-zero mask therefore guarantees that the VCPU will not receive
|
||||
* an upcall activation. The mask is cleared when the VCPU requests
|
||||
* to block: this avoids wakeup-waiting races.
|
||||
*/
|
||||
u8 evtchn_upcall_pending;
|
||||
u8 evtchn_upcall_mask;
|
||||
xen_ulong_t evtchn_pending_sel;
|
||||
struct arch_vcpu_info arch;
|
||||
struct pvclock_vcpu_time_info time;
|
||||
}; /* 64 bytes (x86) */
|
||||
|
||||
/*
|
||||
* Xen/kernel shared data -- pointer provided in start_info.
|
||||
* NB. We expect that this struct is smaller than a page.
|
||||
*/
|
||||
struct shared_info {
|
||||
struct vcpu_info vcpu_info[MAX_VIRT_CPUS];
|
||||
|
||||
/*
|
||||
* A domain can create "event channels" on which it can send and receive
|
||||
* asynchronous event notifications. There are three classes of event that
|
||||
* are delivered by this mechanism:
|
||||
* 1. Bi-directional inter- and intra-domain connections. Domains must
|
||||
* arrange out-of-band to set up a connection (usually by allocating
|
||||
* an unbound 'listener' port and avertising that via a storage service
|
||||
* such as xenstore).
|
||||
* 2. Physical interrupts. A domain with suitable hardware-access
|
||||
* privileges can bind an event-channel port to a physical interrupt
|
||||
* source.
|
||||
* 3. Virtual interrupts ('events'). A domain can bind an event-channel
|
||||
* port to a virtual interrupt source, such as the virtual-timer
|
||||
* device or the emergency console.
|
||||
*
|
||||
* Event channels are addressed by a "port index". Each channel is
|
||||
* associated with two bits of information:
|
||||
* 1. PENDING -- notifies the domain that there is a pending notification
|
||||
* to be processed. This bit is cleared by the guest.
|
||||
* 2. MASK -- if this bit is clear then a 0->1 transition of PENDING
|
||||
* will cause an asynchronous upcall to be scheduled. This bit is only
|
||||
* updated by the guest. It is read-only within Xen. If a channel
|
||||
* becomes pending while the channel is masked then the 'edge' is lost
|
||||
* (i.e., when the channel is unmasked, the guest must manually handle
|
||||
* pending notifications as no upcall will be scheduled by Xen).
|
||||
*
|
||||
* To expedite scanning of pending notifications, any 0->1 pending
|
||||
* transition on an unmasked channel causes a corresponding bit in a
|
||||
* per-vcpu selector word to be set. Each bit in the selector covers a
|
||||
* 'C long' in the PENDING bitfield array.
|
||||
*/
|
||||
xen_ulong_t evtchn_pending[sizeof(xen_ulong_t) * 8];
|
||||
xen_ulong_t evtchn_mask[sizeof(xen_ulong_t) * 8];
|
||||
|
||||
/*
|
||||
* Wallclock time: updated only by control software. Guests should base
|
||||
* their gettimeofday() syscall on this wallclock-base value.
|
||||
*/
|
||||
struct pvclock_wall_clock wc;
|
||||
|
||||
struct arch_shared_info arch;
|
||||
|
||||
};
|
||||
|
||||
#else /* __ASSEMBLY__ */
|
||||
|
||||
/* In assembly code we cannot use C numeric constant suffixes. */
|
||||
#define mk_unsigned_long(x) x
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
#endif /* __XEN_PUBLIC_XEN_H__ */
|
116
include/xen/xenbus.h
Normal file
116
include/xen/xenbus.h
Normal file
@ -0,0 +1,116 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef XENBUS_H__
|
||||
#define XENBUS_H__
|
||||
|
||||
#include <xen/interface/xen.h>
|
||||
#include <xen/interface/io/xenbus.h>
|
||||
|
||||
typedef unsigned long xenbus_transaction_t;
|
||||
#define XBT_NIL ((xenbus_transaction_t)0)
|
||||
|
||||
extern u32 xenbus_evtchn;
|
||||
|
||||
/* Initialize the XenBus system. */
|
||||
void init_xenbus(void);
|
||||
/* Finalize the XenBus system. */
|
||||
void fini_xenbus(void);
|
||||
|
||||
/**
|
||||
* xenbus_read() - Read the value associated with a path.
|
||||
*
|
||||
* Returns a malloc'd error string on failure and sets *value to NULL.
|
||||
* On success, *value is set to a malloc'd copy of the value.
|
||||
*/
|
||||
char *xenbus_read(xenbus_transaction_t xbt, const char *path, char **value);
|
||||
|
||||
char *xenbus_wait_for_state_change(const char *path, XenbusState *state);
|
||||
char *xenbus_switch_state(xenbus_transaction_t xbt, const char *path,
|
||||
XenbusState state);
|
||||
|
||||
/**
|
||||
* xenbus_write() - Associates a value with a path.
|
||||
*
|
||||
* Returns a malloc'd error string on failure.
|
||||
*/
|
||||
char *xenbus_write(xenbus_transaction_t xbt, const char *path,
|
||||
const char *value);
|
||||
|
||||
/**
|
||||
* xenbus_rm() - Removes the value associated with a path.
|
||||
*
|
||||
* Returns a malloc'd error string on failure.
|
||||
*/
|
||||
char *xenbus_rm(xenbus_transaction_t xbt, const char *path);
|
||||
|
||||
/**
|
||||
* xenbus_ls() - List the contents of a directory.
|
||||
*
|
||||
* Returns a malloc'd error string on failure and sets *contents to NULL.
|
||||
* On success, *contents is set to a malloc'd array of pointers to malloc'd
|
||||
* strings. The array is NULL terminated. May block.
|
||||
*/
|
||||
char *xenbus_ls(xenbus_transaction_t xbt, const char *prefix, char ***contents);
|
||||
|
||||
/**
|
||||
* xenbus_get_perms() - Reads permissions associated with a path.
|
||||
*
|
||||
* Returns a malloc'd error string on failure and sets *value to NULL.
|
||||
* On success, *value is set to a malloc'd copy of the value.
|
||||
*/
|
||||
char *xenbus_get_perms(xenbus_transaction_t xbt, const char *path, char **value);
|
||||
|
||||
/**
|
||||
* xenbus_set_perms() - Sets the permissions associated with a path.
|
||||
*
|
||||
* Returns a malloc'd error string on failure.
|
||||
*/
|
||||
char *xenbus_set_perms(xenbus_transaction_t xbt, const char *path, domid_t dom,
|
||||
char perm);
|
||||
|
||||
/**
|
||||
* xenbus_transaction_start() - Start a xenbus transaction.
|
||||
*
|
||||
* Returns the transaction in xbt on success or a malloc'd error string
|
||||
* otherwise.
|
||||
*/
|
||||
char *xenbus_transaction_start(xenbus_transaction_t *xbt);
|
||||
|
||||
/**
|
||||
* xenbus_transaction_end() - End a xenbus transaction.
|
||||
*
|
||||
* Returns a malloc'd error string if it fails. Abort says whether the
|
||||
* transaction should be aborted.
|
||||
* Returns 1 in *retry if the transaction should be retried.
|
||||
*/
|
||||
char *xenbus_transaction_end(xenbus_transaction_t xbt, int abort,
|
||||
int *retry);
|
||||
|
||||
/**
|
||||
* xenbus_read_integer() - Read path and parse it as an integer.
|
||||
*
|
||||
* Returns -1 on error.
|
||||
*/
|
||||
int xenbus_read_integer(const char *path);
|
||||
|
||||
/**
|
||||
* xenbus_read_uuid() - Read path and parse it as 16 byte uuid.
|
||||
*
|
||||
* Returns 1 if read and parsing were successful, 0 if not
|
||||
*/
|
||||
int xenbus_read_uuid(const char *path, unsigned char uuid[16]);
|
||||
|
||||
/**
|
||||
* xenbus_printf() - Contraction of snprintf and xenbus_write(path/node).
|
||||
*/
|
||||
char *xenbus_printf(xenbus_transaction_t xbt,
|
||||
const char *node, const char *path,
|
||||
const char *fmt, ...)
|
||||
__attribute__((__format__(printf, 4, 5)));
|
||||
|
||||
/**
|
||||
* xenbus_get_self_id() - Utility function to figure out our domain id
|
||||
*/
|
||||
domid_t xenbus_get_self_id(void);
|
||||
|
||||
#endif /* XENBUS_H__ */
|
@ -80,6 +80,10 @@ config SPL_SPRINTF
|
||||
config TPL_SPRINTF
|
||||
bool
|
||||
|
||||
config SSCANF
|
||||
bool
|
||||
default n
|
||||
|
||||
config STRTO
|
||||
bool
|
||||
default y
|
||||
|
@ -123,6 +123,7 @@ else
|
||||
# Main U-Boot always uses the full printf support
|
||||
obj-y += vsprintf.o strto.o
|
||||
obj-$(CONFIG_OID_REGISTRY) += oid_registry.o
|
||||
obj-$(CONFIG_SSCANF) += sscanf.o
|
||||
endif
|
||||
|
||||
obj-y += date.o
|
||||
|
823
lib/sscanf.c
Normal file
823
lib/sscanf.c
Normal file
@ -0,0 +1,823 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
* Copyright (c) 1990, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Chris Torek.
|
||||
*
|
||||
* Copyright (c) 2011 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
* Portions of this software were developed by David Chisnall
|
||||
* under sponsorship from the FreeBSD Foundation.
|
||||
*
|
||||
* Author: Juergen Gross <jgross@suse.com>
|
||||
* Date: Jun 2016
|
||||
*/
|
||||
|
||||
#if !defined HAVE_LIBC
|
||||
|
||||
#include <os.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <vsprintf.h>
|
||||
#include <linux/string.h>
|
||||
#include <malloc.h>
|
||||
#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
|
||||
|
||||
/**
|
||||
* struct str_info - Input string parameters
|
||||
* @neg: negative number or not
|
||||
* 0 - not negative
|
||||
* 1 - negative
|
||||
* @any: set any if any `digits' consumed; make it negative to indicate
|
||||
* overflow
|
||||
* @acc: accumulated value
|
||||
*/
|
||||
struct str_info {
|
||||
int neg, any;
|
||||
u64 acc;
|
||||
};
|
||||
|
||||
/**
|
||||
* str_to_int_convert() - Write string data to structure
|
||||
* @nptr: pointer to string
|
||||
* @base: number's base
|
||||
* @unsign: describes what integer is expected
|
||||
* 0 - not unsigned
|
||||
* 1 - unsigned
|
||||
*
|
||||
* Ignores `locale' stuff. Assumes that the upper and lower case
|
||||
* alphabets and digits are each contiguous.
|
||||
*
|
||||
* Return: struct str_info *, which contains string data to future process
|
||||
*/
|
||||
static struct str_info *
|
||||
str_to_int_convert(const char **nptr, int base, unsigned int unsign)
|
||||
{
|
||||
const char *s = *nptr;
|
||||
u64 acc;
|
||||
unsigned char c;
|
||||
u64 cutoff;
|
||||
int neg, any, cutlim;
|
||||
u64 qbase;
|
||||
struct str_info *info;
|
||||
|
||||
/*
|
||||
* Skip white space and pick up leading +/- sign if any.
|
||||
* If base is 0, allow 0x for hex and 0 for octal, else
|
||||
* assume decimal; if base is already 16, allow 0x.
|
||||
*/
|
||||
info = (struct str_info *)malloc(sizeof(struct str_info));
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
do {
|
||||
c = *s++;
|
||||
} while (isspace(c));
|
||||
if (c == '-') {
|
||||
neg = 1;
|
||||
c = *s++;
|
||||
} else {
|
||||
neg = 0;
|
||||
if (c == '+')
|
||||
c = *s++;
|
||||
}
|
||||
if ((base == 0 || base == 16) &&
|
||||
c == '0' && (*s == 'x' || *s == 'X')) {
|
||||
c = s[1];
|
||||
s += 2;
|
||||
base = 16;
|
||||
}
|
||||
if (base == 0)
|
||||
base = c == '0' ? 8 : 10;
|
||||
|
||||
/*
|
||||
* Compute the cutoff value between legal numbers and illegal
|
||||
* numbers. That is the largest legal value, divided by the
|
||||
* base. An input number that is greater than this value, if
|
||||
* followed by a legal input character, is too big. One that
|
||||
* is equal to this value may be valid or not; the limit
|
||||
* between valid and invalid numbers is then based on the last
|
||||
* digit. For instance, if the range for quads is
|
||||
* [-9223372036854775808..9223372036854775807] and the input base
|
||||
* is 10, cutoff will be set to 922337203685477580 and cutlim to
|
||||
* either 7 (neg==0) or 8 (neg==1), meaning that if we have
|
||||
* accumulated a value > 922337203685477580, or equal but the
|
||||
* next digit is > 7 (or 8), the number is too big, and we will
|
||||
* return a range error.
|
||||
*
|
||||
* Set any if any `digits' consumed; make it negative to indicate
|
||||
* overflow.
|
||||
*/
|
||||
qbase = (unsigned int)base;
|
||||
|
||||
if (!unsign) {
|
||||
cutoff = neg ? (u64)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX : LLONG_MAX;
|
||||
cutlim = cutoff % qbase;
|
||||
cutoff /= qbase;
|
||||
} else {
|
||||
cutoff = (u64)ULLONG_MAX / qbase;
|
||||
cutlim = (u64)ULLONG_MAX % qbase;
|
||||
}
|
||||
|
||||
for (acc = 0, any = 0;; c = *s++) {
|
||||
if (!isascii(c))
|
||||
break;
|
||||
if (isdigit(c))
|
||||
c -= '0';
|
||||
else if (isalpha(c))
|
||||
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
||||
else
|
||||
break;
|
||||
if (c >= base)
|
||||
break;
|
||||
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) {
|
||||
any = -1;
|
||||
} else {
|
||||
any = 1;
|
||||
acc *= qbase;
|
||||
acc += c;
|
||||
}
|
||||
}
|
||||
|
||||
info->any = any;
|
||||
info->neg = neg;
|
||||
info->acc = acc;
|
||||
|
||||
*nptr = s;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* strtoq() - Convert a string to a quad integer
|
||||
* @nptr: pointer to string
|
||||
* @endptr: pointer to number's end in the string
|
||||
* @base: number's base
|
||||
*
|
||||
* Return: s64 quad integer number converted from input string
|
||||
*/
|
||||
static s64
|
||||
strtoq(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
const char *s = nptr;
|
||||
u64 acc;
|
||||
int unsign = 0;
|
||||
struct str_info *info;
|
||||
|
||||
info = str_to_int_convert(&s, base, unsign);
|
||||
if (!info)
|
||||
return -1;
|
||||
|
||||
acc = info->acc;
|
||||
|
||||
if (info->any < 0)
|
||||
acc = info->neg ? LLONG_MIN : LLONG_MAX;
|
||||
else if (info->neg)
|
||||
acc = -acc;
|
||||
if (endptr != 0)
|
||||
*endptr = __DECONST(char *, info->any ? s - 1 : nptr);
|
||||
|
||||
free(info);
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
/**
|
||||
* strtouq() - Convert a string to an unsigned quad integer
|
||||
* @nptr: pointer to string
|
||||
* @endptr: pointer to number's end in the string
|
||||
* @base: number's base
|
||||
*
|
||||
* Return: s64 unsigned quad integer number converted from
|
||||
* input string
|
||||
*/
|
||||
u64
|
||||
strtouq(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
const char *s = nptr;
|
||||
u64 acc;
|
||||
int unsign = 1;
|
||||
struct str_info *info;
|
||||
|
||||
info = str_to_int_convert(&s, base, unsign);
|
||||
if (!info)
|
||||
return -1;
|
||||
|
||||
acc = info->acc;
|
||||
|
||||
if (info->any < 0)
|
||||
acc = ULLONG_MAX;
|
||||
else if (info->neg)
|
||||
acc = -acc;
|
||||
if (endptr != 0)
|
||||
*endptr = __DECONST(char *, info->any ? s - 1 : nptr);
|
||||
|
||||
free(info);
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
/**
|
||||
* __sccl() - Fill in the given table from the scanset at the given format
|
||||
* (just after `[')
|
||||
* @tab: table to fill in
|
||||
* @fmt: format of buffer
|
||||
*
|
||||
* The table has a 1 wherever characters should be considered part of the
|
||||
* scanset.
|
||||
*
|
||||
* Return: pointer to the character past the closing `]'
|
||||
*/
|
||||
static const u_char *
|
||||
__sccl(char *tab, const u_char *fmt)
|
||||
{
|
||||
int c, n, v;
|
||||
|
||||
/* first `clear' the whole table */
|
||||
c = *fmt++; /* first char hat => negated scanset */
|
||||
if (c == '^') {
|
||||
v = 1; /* default => accept */
|
||||
c = *fmt++; /* get new first char */
|
||||
} else {
|
||||
v = 0; /* default => reject */
|
||||
}
|
||||
|
||||
/* XXX: Will not work if sizeof(tab*) > sizeof(char) */
|
||||
for (n = 0; n < 256; n++)
|
||||
tab[n] = v; /* memset(tab, v, 256) */
|
||||
|
||||
if (c == 0)
|
||||
return (fmt - 1);/* format ended before closing ] */
|
||||
|
||||
/*
|
||||
* Now set the entries corresponding to the actual scanset
|
||||
* to the opposite of the above.
|
||||
*
|
||||
* The first character may be ']' (or '-') without being special;
|
||||
* the last character may be '-'.
|
||||
*/
|
||||
v = 1 - v;
|
||||
for (;;) {
|
||||
tab[c] = v; /* take character c */
|
||||
doswitch:
|
||||
n = *fmt++; /* and examine the next */
|
||||
switch (n) {
|
||||
case 0: /* format ended too soon */
|
||||
return (fmt - 1);
|
||||
|
||||
case '-':
|
||||
/*
|
||||
* A scanset of the form
|
||||
* [01+-]
|
||||
* is defined as `the digit 0, the digit 1,
|
||||
* the character +, the character -', but
|
||||
* the effect of a scanset such as
|
||||
* [a-zA-Z0-9]
|
||||
* is implementation defined. The V7 Unix
|
||||
* scanf treats `a-z' as `the letters a through
|
||||
* z', but treats `a-a' as `the letter a, the
|
||||
* character -, and the letter a'.
|
||||
*
|
||||
* For compatibility, the `-' is not considerd
|
||||
* to define a range if the character following
|
||||
* it is either a close bracket (required by ANSI)
|
||||
* or is not numerically greater than the character
|
||||
* we just stored in the table (c).
|
||||
*/
|
||||
n = *fmt;
|
||||
if (n == ']' || n < c) {
|
||||
c = '-';
|
||||
break; /* resume the for(;;) */
|
||||
}
|
||||
fmt++;
|
||||
/* fill in the range */
|
||||
do {
|
||||
tab[++c] = v;
|
||||
} while (c < n);
|
||||
c = n;
|
||||
/*
|
||||
* Alas, the V7 Unix scanf also treats formats
|
||||
* such as [a-c-e] as `the letters a through e'.
|
||||
* This too is permitted by the standard....
|
||||
*/
|
||||
goto doswitch;
|
||||
break;
|
||||
|
||||
case ']': /* end of scanset */
|
||||
return (fmt);
|
||||
|
||||
default: /* just another character */
|
||||
c = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
/**
|
||||
* vsscanf - Unformat a buffer into a list of arguments
|
||||
* @buf: input buffer
|
||||
* @fmt: format of buffer
|
||||
* @args: arguments
|
||||
*/
|
||||
#define BUF 32 /* Maximum length of numeric string. */
|
||||
|
||||
/*
|
||||
* Flags used during conversion.
|
||||
*/
|
||||
#define LONG 0x01 /* l: long or double */
|
||||
#define SHORT 0x04 /* h: short */
|
||||
#define SUPPRESS 0x08 /* suppress assignment */
|
||||
#define POINTER 0x10 /* weird %p pointer (`fake hex') */
|
||||
#define NOSKIP 0x20 /* do not skip blanks */
|
||||
#define QUAD 0x400
|
||||
#define SHORTSHORT 0x4000 /** hh: char */
|
||||
|
||||
/*
|
||||
* The following are used in numeric conversions only:
|
||||
* SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
|
||||
* SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
|
||||
*/
|
||||
#define SIGNOK 0x40 /* +/- is (still) legal */
|
||||
#define NDIGITS 0x80 /* no digits detected */
|
||||
|
||||
#define DPTOK 0x100 /* (float) decimal point is still legal */
|
||||
#define EXPOK 0x200 /* (float) exponent (e+3, etc) still legal */
|
||||
|
||||
#define PFXOK 0x100 /* 0x prefix is (still) legal */
|
||||
#define NZDIGITS 0x200 /* no zero digits detected */
|
||||
|
||||
/*
|
||||
* Conversion types.
|
||||
*/
|
||||
#define CT_CHAR 0 /* %c conversion */
|
||||
#define CT_CCL 1 /* %[...] conversion */
|
||||
#define CT_STRING 2 /* %s conversion */
|
||||
#define CT_INT 3 /* integer, i.e., strtoq or strtouq */
|
||||
typedef u64 (*ccfntype)(const char *, char **, int);
|
||||
|
||||
int
|
||||
vsscanf(const char *inp, char const *fmt0, va_list ap)
|
||||
{
|
||||
int inr;
|
||||
const u_char *fmt = (const u_char *)fmt0;
|
||||
int c; /* character from format, or conversion */
|
||||
size_t width; /* field width, or 0 */
|
||||
char *p; /* points into all kinds of strings */
|
||||
int n; /* handy integer */
|
||||
int flags; /* flags as defined above */
|
||||
char *p0; /* saves original value of p when necessary */
|
||||
int nassigned; /* number of fields assigned */
|
||||
int nconversions; /* number of conversions */
|
||||
int nread; /* number of characters consumed from fp */
|
||||
int base; /* base argument to strtoq/strtouq */
|
||||
ccfntype ccfn; /* conversion function (strtoq/strtouq) */
|
||||
char ccltab[256]; /* character class table for %[...] */
|
||||
char buf[BUF]; /* buffer for numeric conversions */
|
||||
|
||||
/* `basefix' is used to avoid `if' tests in the integer scanner */
|
||||
static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
||||
12, 13, 14, 15, 16 };
|
||||
|
||||
inr = strlen(inp);
|
||||
|
||||
nassigned = 0;
|
||||
nconversions = 0;
|
||||
nread = 0;
|
||||
base = 0; /* XXX just to keep gcc happy */
|
||||
ccfn = NULL; /* XXX just to keep gcc happy */
|
||||
for (;;) {
|
||||
c = *fmt++;
|
||||
if (c == 0)
|
||||
return (nassigned);
|
||||
if (isspace(c)) {
|
||||
while (inr > 0 && isspace(*inp))
|
||||
nread++, inr--, inp++;
|
||||
continue;
|
||||
}
|
||||
if (c != '%')
|
||||
goto literal;
|
||||
width = 0;
|
||||
flags = 0;
|
||||
/*
|
||||
* switch on the format. continue if done;
|
||||
* break once format type is derived.
|
||||
*/
|
||||
again: c = *fmt++;
|
||||
switch (c) {
|
||||
case '%':
|
||||
literal:
|
||||
if (inr <= 0)
|
||||
goto input_failure;
|
||||
if (*inp != c)
|
||||
goto match_failure;
|
||||
inr--, inp++;
|
||||
nread++;
|
||||
continue;
|
||||
|
||||
case '*':
|
||||
flags |= SUPPRESS;
|
||||
goto again;
|
||||
case 'l':
|
||||
if (flags & LONG) {
|
||||
flags &= ~LONG;
|
||||
flags |= QUAD;
|
||||
} else {
|
||||
flags |= LONG;
|
||||
}
|
||||
goto again;
|
||||
case 'q':
|
||||
flags |= QUAD;
|
||||
goto again;
|
||||
case 'h':
|
||||
if (flags & SHORT) {
|
||||
flags &= ~SHORT;
|
||||
flags |= SHORTSHORT;
|
||||
} else {
|
||||
flags |= SHORT;
|
||||
}
|
||||
goto again;
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
width = width * 10 + c - '0';
|
||||
goto again;
|
||||
|
||||
/*
|
||||
* Conversions.
|
||||
*
|
||||
*/
|
||||
case 'd':
|
||||
c = CT_INT;
|
||||
ccfn = (ccfntype)strtoq;
|
||||
base = 10;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
c = CT_INT;
|
||||
ccfn = (ccfntype)strtoq;
|
||||
base = 0;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
c = CT_INT;
|
||||
ccfn = strtouq;
|
||||
base = 8;
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
c = CT_INT;
|
||||
ccfn = strtouq;
|
||||
base = 10;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
flags |= PFXOK; /* enable 0x prefixing */
|
||||
c = CT_INT;
|
||||
ccfn = strtouq;
|
||||
base = 16;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
c = CT_STRING;
|
||||
break;
|
||||
|
||||
case '[':
|
||||
fmt = __sccl(ccltab, fmt);
|
||||
flags |= NOSKIP;
|
||||
c = CT_CCL;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
flags |= NOSKIP;
|
||||
c = CT_CHAR;
|
||||
break;
|
||||
|
||||
case 'p': /* pointer format is like hex */
|
||||
flags |= POINTER | PFXOK;
|
||||
c = CT_INT;
|
||||
ccfn = strtouq;
|
||||
base = 16;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
nconversions++;
|
||||
if (flags & SUPPRESS) /* ??? */
|
||||
continue;
|
||||
if (flags & SHORTSHORT)
|
||||
*va_arg(ap, char *) = nread;
|
||||
else if (flags & SHORT)
|
||||
*va_arg(ap, short *) = nread;
|
||||
else if (flags & LONG)
|
||||
*va_arg(ap, long *) = nread;
|
||||
else if (flags & QUAD)
|
||||
*va_arg(ap, s64 *) = nread;
|
||||
else
|
||||
*va_arg(ap, int *) = nread;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a conversion that requires input.
|
||||
*/
|
||||
if (inr <= 0)
|
||||
goto input_failure;
|
||||
|
||||
/*
|
||||
* Consume leading white space, except for formats
|
||||
* that suppress this.
|
||||
*/
|
||||
if ((flags & NOSKIP) == 0) {
|
||||
while (isspace(*inp)) {
|
||||
nread++;
|
||||
if (--inr > 0)
|
||||
inp++;
|
||||
else
|
||||
goto input_failure;
|
||||
}
|
||||
/*
|
||||
* Note that there is at least one character in
|
||||
* the buffer, so conversions that do not set NOSKIP
|
||||
* can no longer result in an input failure.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the conversion.
|
||||
*/
|
||||
switch (c) {
|
||||
case CT_CHAR:
|
||||
/* scan arbitrary characters (sets NOSKIP) */
|
||||
if (width == 0)
|
||||
width = 1;
|
||||
if (flags & SUPPRESS) {
|
||||
size_t sum = 0;
|
||||
|
||||
if ((n = inr) < width) {
|
||||
sum += n;
|
||||
width -= n;
|
||||
inp += n;
|
||||
if (sum == 0)
|
||||
goto input_failure;
|
||||
} else {
|
||||
sum += width;
|
||||
inr -= width;
|
||||
inp += width;
|
||||
}
|
||||
nread += sum;
|
||||
} else {
|
||||
memcpy(va_arg(ap, char *), inp, width);
|
||||
inr -= width;
|
||||
inp += width;
|
||||
nread += width;
|
||||
nassigned++;
|
||||
}
|
||||
nconversions++;
|
||||
break;
|
||||
|
||||
case CT_CCL:
|
||||
/* scan a (nonempty) character class (sets NOSKIP) */
|
||||
if (width == 0)
|
||||
width = (size_t)~0; /* `infinity' */
|
||||
/* take only those things in the class */
|
||||
if (flags & SUPPRESS) {
|
||||
n = 0;
|
||||
while (ccltab[(unsigned char)*inp]) {
|
||||
n++, inr--, inp++;
|
||||
if (--width == 0)
|
||||
break;
|
||||
if (inr <= 0) {
|
||||
if (n == 0)
|
||||
goto input_failure;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (n == 0)
|
||||
goto match_failure;
|
||||
} else {
|
||||
p = va_arg(ap, char *);
|
||||
p0 = p;
|
||||
while (ccltab[(unsigned char)*inp]) {
|
||||
inr--;
|
||||
*p++ = *inp++;
|
||||
if (--width == 0)
|
||||
break;
|
||||
if (inr <= 0) {
|
||||
if (p == p0)
|
||||
goto input_failure;
|
||||
break;
|
||||
}
|
||||
}
|
||||
n = p - p0;
|
||||
if (n == 0)
|
||||
goto match_failure;
|
||||
*p = 0;
|
||||
nassigned++;
|
||||
}
|
||||
nread += n;
|
||||
nconversions++;
|
||||
break;
|
||||
|
||||
case CT_STRING:
|
||||
/* like CCL, but zero-length string OK, & no NOSKIP */
|
||||
if (width == 0)
|
||||
width = (size_t)~0;
|
||||
if (flags & SUPPRESS) {
|
||||
n = 0;
|
||||
while (!isspace(*inp)) {
|
||||
n++, inr--, inp++;
|
||||
if (--width == 0)
|
||||
break;
|
||||
if (inr <= 0)
|
||||
break;
|
||||
}
|
||||
nread += n;
|
||||
} else {
|
||||
p = va_arg(ap, char *);
|
||||
p0 = p;
|
||||
while (!isspace(*inp)) {
|
||||
inr--;
|
||||
*p++ = *inp++;
|
||||
if (--width == 0)
|
||||
break;
|
||||
if (inr <= 0)
|
||||
break;
|
||||
}
|
||||
*p = 0;
|
||||
nread += p - p0;
|
||||
nassigned++;
|
||||
}
|
||||
nconversions++;
|
||||
continue;
|
||||
|
||||
case CT_INT:
|
||||
/* scan an integer as if by strtoq/strtouq */
|
||||
#ifdef hardway
|
||||
if (width == 0 || width > sizeof(buf) - 1)
|
||||
width = sizeof(buf) - 1;
|
||||
#else
|
||||
/* size_t is unsigned, hence this optimisation */
|
||||
if (--width > sizeof(buf) - 2)
|
||||
width = sizeof(buf) - 2;
|
||||
width++;
|
||||
#endif
|
||||
flags |= SIGNOK | NDIGITS | NZDIGITS;
|
||||
for (p = buf; width; width--) {
|
||||
c = *inp;
|
||||
/*
|
||||
* Switch on the character; `goto ok'
|
||||
* if we accept it as a part of number.
|
||||
*/
|
||||
switch (c) {
|
||||
/*
|
||||
* The digit 0 is always legal, but is
|
||||
* special. For %i conversions, if no
|
||||
* digits (zero or nonzero) have been
|
||||
* scanned (only signs), we will have
|
||||
* base==0. In that case, we should set
|
||||
* it to 8 and enable 0x prefixing.
|
||||
* Also, if we have not scanned zero digits
|
||||
* before this, do not turn off prefixing
|
||||
* (someone else will turn it off if we
|
||||
* have scanned any nonzero digits).
|
||||
*/
|
||||
case '0':
|
||||
if (base == 0) {
|
||||
base = 8;
|
||||
flags |= PFXOK;
|
||||
}
|
||||
if (flags & NZDIGITS)
|
||||
flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
|
||||
else
|
||||
flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
||||
goto ok;
|
||||
|
||||
/* 1 through 7 always legal */
|
||||
case '1': case '2': case '3':
|
||||
case '4': case '5': case '6': case '7':
|
||||
base = basefix[base];
|
||||
flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
||||
goto ok;
|
||||
|
||||
/* digits 8 and 9 ok iff decimal or hex */
|
||||
case '8': case '9':
|
||||
base = basefix[base];
|
||||
if (base <= 8)
|
||||
break; /* not legal here */
|
||||
flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
||||
goto ok;
|
||||
|
||||
/* letters ok iff hex */
|
||||
case 'A': case 'B': case 'C':
|
||||
case 'D': case 'E': case 'F':
|
||||
case 'a': case 'b': case 'c':
|
||||
case 'd': case 'e': case 'f':
|
||||
/* no need to fix base here */
|
||||
if (base <= 10)
|
||||
break; /* not legal here */
|
||||
flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
||||
goto ok;
|
||||
|
||||
/* sign ok only as first character */
|
||||
case '+': case '-':
|
||||
if (flags & SIGNOK) {
|
||||
flags &= ~SIGNOK;
|
||||
goto ok;
|
||||
}
|
||||
break;
|
||||
|
||||
/* x ok iff flag still set & 2nd char */
|
||||
case 'x': case 'X':
|
||||
if (flags & PFXOK && p == buf + 1) {
|
||||
base = 16; /* if %i */
|
||||
flags &= ~PFXOK;
|
||||
goto ok;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we got here, c is not a legal character
|
||||
* for a number. Stop accumulating digits.
|
||||
*/
|
||||
break;
|
||||
ok:
|
||||
/*
|
||||
* c is legal: store it and look at the next.
|
||||
*/
|
||||
*p++ = c;
|
||||
if (--inr > 0)
|
||||
inp++;
|
||||
else
|
||||
break; /* end of input */
|
||||
}
|
||||
/*
|
||||
* If we had only a sign, it is no good; push
|
||||
* back the sign. If the number ends in `x',
|
||||
* it was [sign] '' 'x', so push back the x
|
||||
* and treat it as [sign] ''.
|
||||
*/
|
||||
if (flags & NDIGITS) {
|
||||
if (p > buf) {
|
||||
inp--;
|
||||
inr++;
|
||||
}
|
||||
goto match_failure;
|
||||
}
|
||||
c = ((u_char *)p)[-1];
|
||||
if (c == 'x' || c == 'X') {
|
||||
--p;
|
||||
inp--;
|
||||
inr++;
|
||||
}
|
||||
if ((flags & SUPPRESS) == 0) {
|
||||
u64 res;
|
||||
|
||||
*p = 0;
|
||||
res = (*ccfn)(buf, (char **)NULL, base);
|
||||
if (flags & POINTER)
|
||||
*va_arg(ap, void **) =
|
||||
(void *)(uintptr_t)res;
|
||||
else if (flags & SHORTSHORT)
|
||||
*va_arg(ap, char *) = res;
|
||||
else if (flags & SHORT)
|
||||
*va_arg(ap, short *) = res;
|
||||
else if (flags & LONG)
|
||||
*va_arg(ap, long *) = res;
|
||||
else if (flags & QUAD)
|
||||
*va_arg(ap, s64 *) = res;
|
||||
else
|
||||
*va_arg(ap, int *) = res;
|
||||
nassigned++;
|
||||
}
|
||||
nread += p - buf;
|
||||
nconversions++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
input_failure:
|
||||
return (nconversions != 0 ? nassigned : -1);
|
||||
match_failure:
|
||||
return (nassigned);
|
||||
}
|
||||
|
||||
/**
|
||||
* sscanf - Unformat a buffer into a list of arguments
|
||||
* @buf: input buffer
|
||||
* @fmt: formatting of buffer
|
||||
* @...: resulting arguments
|
||||
*/
|
||||
int sscanf(const char *buf, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i;
|
||||
|
||||
va_start(args, fmt);
|
||||
i = vsscanf(buf, fmt, args);
|
||||
va_end(args);
|
||||
return i;
|
||||
}
|
||||
|
||||
#endif
|
@ -6,6 +6,7 @@ obj-y += cmd_ut_lib.o
|
||||
obj-$(CONFIG_EFI_SECURE_BOOT) += efi_image_region.o
|
||||
obj-y += hexdump.o
|
||||
obj-y += lmb.o
|
||||
obj-y += sscanf.o
|
||||
obj-y += string.o
|
||||
obj-$(CONFIG_ERRNO_STR) += test_errno_str.o
|
||||
obj-$(CONFIG_UT_LIB_ASN1) += asn1.o
|
||||
|
174
test/lib/sscanf.c
Normal file
174
test/lib/sscanf.c
Normal file
@ -0,0 +1,174 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2002, Uwe Bonnes
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2016, The Tor Project, Inc.
|
||||
* Copyright (c) 2020, EPAM Systems Inc.
|
||||
*
|
||||
* Unit tests for sscanf() function
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <log.h>
|
||||
#include <test/lib.h>
|
||||
#include <test/test.h>
|
||||
#include <test/ut.h>
|
||||
|
||||
#define EOF -1
|
||||
|
||||
/**
|
||||
* lib_sscanf() - unit test for sscanf()
|
||||
* @uts: unit test state
|
||||
*
|
||||
* Test sscanf() with varied parameters in different combinations passed
|
||||
* as arguments.
|
||||
*
|
||||
* Return: 0 - success
|
||||
* 1 - failure
|
||||
*/
|
||||
static int lib_sscanf(struct unit_test_state *uts)
|
||||
{
|
||||
char buffer[100], buffer1[100];
|
||||
int result, ret;
|
||||
static const char pname[] = " Hello World!\n";
|
||||
int hour = 21, min = 59, sec = 20;
|
||||
int number, number_so_far;
|
||||
unsigned int u1, u2, u3;
|
||||
char s1[20], s2[10], s3[10], ch;
|
||||
int r, int1, int2;
|
||||
long lng1;
|
||||
|
||||
/* check EOF */
|
||||
strcpy(buffer, "");
|
||||
ret = sscanf(buffer, "%d", &result);
|
||||
ut_asserteq(ret, EOF);
|
||||
|
||||
/* check %x */
|
||||
strcpy(buffer, "0x519");
|
||||
ut_asserteq(sscanf(buffer, "%x", &result), 1);
|
||||
ut_asserteq(result, 0x519);
|
||||
|
||||
strcpy(buffer, "0x51a");
|
||||
ut_asserteq(sscanf(buffer, "%x", &result), 1);
|
||||
ut_asserteq(result, 0x51a);
|
||||
|
||||
strcpy(buffer, "0x51g");
|
||||
ut_asserteq(sscanf(buffer, "%x", &result), 1);
|
||||
ut_asserteq(result, 0x51);
|
||||
|
||||
/* check strings */
|
||||
ret = sprintf(buffer, " %s", pname);
|
||||
ret = sscanf(buffer, "%*c%[^\n]", buffer1);
|
||||
ut_asserteq(ret, 1);
|
||||
ut_asserteq(strncmp(pname, buffer1, strlen(buffer1)), 0);
|
||||
|
||||
/* check digits */
|
||||
ret = sprintf(buffer, "%d:%d:%d", hour, min, sec);
|
||||
ret = sscanf(buffer, "%d%n", &number, &number_so_far);
|
||||
ut_asserteq(ret, 1);
|
||||
ut_asserteq(number, hour);
|
||||
ut_asserteq(number_so_far, 2);
|
||||
|
||||
ret = sscanf(buffer + 2, "%*c%n", &number_so_far);
|
||||
ut_asserteq(ret, 0);
|
||||
ut_asserteq(number_so_far, 1);
|
||||
|
||||
/* Check %i */
|
||||
strcpy(buffer, "123");
|
||||
ret = sscanf(buffer, "%i", &result);
|
||||
ut_asserteq(ret, 1);
|
||||
ut_asserteq(result, 123);
|
||||
ret = sscanf(buffer, "%d", &result);
|
||||
ut_asserteq(ret, 1);
|
||||
ut_asserteq(result, 123);
|
||||
|
||||
ut_asserteq(0, sscanf("hello world", "hello world"));
|
||||
ut_asserteq(0, sscanf("hello world", "good bye"));
|
||||
/* Excess data */
|
||||
ut_asserteq(0, sscanf("hello 3", "%u", &u1)); /* have to match the start */
|
||||
ut_asserteq(1, sscanf("3 hello", "%u", &u1)); /* but trailing is alright */
|
||||
|
||||
/* Numbers (ie. %u) */
|
||||
ut_asserteq(0, sscanf("hello world 3", "hello worlb %u", &u1)); /* d vs b */
|
||||
ut_asserteq(1, sscanf("12345", "%u", &u1));
|
||||
ut_asserteq(12345u, u1);
|
||||
ut_asserteq(1, sscanf("0", "%u", &u1));
|
||||
ut_asserteq(0u, u1);
|
||||
ut_asserteq(1, sscanf("0000", "%u", &u2));
|
||||
ut_asserteq(0u, u2);
|
||||
ut_asserteq(0, sscanf("A", "%u", &u1)); /* bogus number */
|
||||
|
||||
/* Numbers with size (eg. %2u) */
|
||||
ut_asserteq(2, sscanf("123456", "%2u%u", &u1, &u2));
|
||||
ut_asserteq(12u, u1);
|
||||
ut_asserteq(3456u, u2);
|
||||
ut_asserteq(1, sscanf("123456", "%8u", &u1));
|
||||
ut_asserteq(123456u, u1);
|
||||
ut_asserteq(1, sscanf("123457 ", "%8u", &u1));
|
||||
ut_asserteq(123457u, u1);
|
||||
ut_asserteq(3, sscanf("!12:3:456", "!%2u:%2u:%3u", &u1, &u2, &u3));
|
||||
ut_asserteq(12u, u1);
|
||||
ut_asserteq(3u, u2);
|
||||
ut_asserteq(456u, u3);
|
||||
ut_asserteq(3, sscanf("67:8:099", "%2u:%2u:%3u", &u1, &u2, &u3)); /* 0s */
|
||||
ut_asserteq(67u, u1);
|
||||
ut_asserteq(8u, u2);
|
||||
ut_asserteq(99u, u3);
|
||||
/* Arbitrary amounts of 0-padding are okay */
|
||||
ut_asserteq(3, sscanf("12:03:000000000000000099", "%2u:%2u:%u", &u1, &u2, &u3));
|
||||
ut_asserteq(12u, u1);
|
||||
ut_asserteq(3u, u2);
|
||||
ut_asserteq(99u, u3);
|
||||
|
||||
/* Hex (ie. %x) */
|
||||
ut_asserteq(3, sscanf("1234 02aBcdEf ff", "%x %x %x", &u1, &u2, &u3));
|
||||
ut_asserteq(0x1234, u1);
|
||||
ut_asserteq(0x2ABCDEF, u2);
|
||||
ut_asserteq(0xFF, u3);
|
||||
/* Width works on %x */
|
||||
ut_asserteq(3, sscanf("f00dcafe444", "%4x%4x%u", &u1, &u2, &u3));
|
||||
ut_asserteq(0xf00d, u1);
|
||||
ut_asserteq(0xcafe, u2);
|
||||
ut_asserteq(444, u3);
|
||||
|
||||
/* Literal '%' (ie. '%%') */
|
||||
ut_asserteq(1, sscanf("99% fresh", "%3u%% fresh", &u1));
|
||||
ut_asserteq(99, u1);
|
||||
ut_asserteq(0, sscanf("99 fresh", "%% %3u %s", &u1, s1));
|
||||
ut_asserteq(1, sscanf("99 fresh", "%3u%% %s", &u1, s1));
|
||||
ut_asserteq(2, sscanf("99 fresh", "%3u %5s %%", &u1, s1));
|
||||
ut_asserteq(99, u1);
|
||||
ut_asserteq_str(s1, "fresh");
|
||||
ut_asserteq(1, sscanf("% boo", "%% %3s", s1));
|
||||
ut_asserteq_str("boo", s1);
|
||||
|
||||
/* Strings (ie. %s) */
|
||||
ut_asserteq(2, sscanf("hello", "%3s%7s", s1, s2));
|
||||
ut_asserteq_str(s1, "hel");
|
||||
ut_asserteq_str(s2, "lo");
|
||||
ut_asserteq(2, sscanf("WD40", "%2s%u", s3, &u1)); /* %s%u */
|
||||
ut_asserteq_str(s3, "WD");
|
||||
ut_asserteq(40, u1);
|
||||
ut_asserteq(2, sscanf("WD40", "%3s%u", s3, &u1)); /* %s%u */
|
||||
ut_asserteq_str(s3, "WD4");
|
||||
ut_asserteq(0, u1);
|
||||
ut_asserteq(2, sscanf("76trombones", "%6u%9s", &u1, s1)); /* %u%s */
|
||||
ut_asserteq(76, u1);
|
||||
ut_asserteq_str(s1, "trombones");
|
||||
|
||||
ut_asserteq(3, sscanf("1.2.3", "%u.%u.%u%c", &u1, &u2, &u3, &ch));
|
||||
ut_asserteq(4, sscanf("1.2.3 foobar", "%u.%u.%u%c", &u1, &u2, &u3, &ch));
|
||||
ut_asserteq(' ', ch);
|
||||
|
||||
r = sscanf("12345 -67890 -1", "%d %ld %d", &int1, &lng1, &int2);
|
||||
ut_asserteq(r, 3);
|
||||
ut_asserteq(int1, 12345);
|
||||
ut_asserteq(lng1, -67890);
|
||||
ut_asserteq(int2, -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIB_TEST(lib_sscanf, 0);
|
Loading…
Reference in New Issue
Block a user