diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common')
268 files changed, 30439 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/COPYING.MIT b/meta-openbmc-mods/meta-common/COPYING.MIT new file mode 100644 index 000000000..89de35479 --- /dev/null +++ b/meta-openbmc-mods/meta-common/COPYING.MIT @@ -0,0 +1,17 @@ +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. diff --git a/meta-openbmc-mods/meta-common/README b/meta-openbmc-mods/meta-common/README new file mode 100644 index 000000000..9d4a8a1e6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/README @@ -0,0 +1,64 @@ +This README file contains information on the contents of the +common layer. + +Please see the corresponding sections below for details. + + +Dependencies +============ + +This layer depends on: + + URI: git://git.openembedded.org/bitbake + branch: master + + URI: git://git.openembedded.org/openembedded-core + layers: meta + branch: master + + URI: git://git.yoctoproject.org/xxxx + layers: xxxx + branch: master + + +Patches +======= + +Please submit any patches against the common layer to the +xxxx mailing list (xxxx@zzzz.org) and cc: the maintainer: + +Maintainer: XXX YYYYYY <xxx.yyyyyy@zzzzz.com> + + +Table of Contents +================= + + I. Adding the common layer to your build + II. Misc + + +I. Adding the common layer to your build +================================================= + +--- replace with specific instructions for the common layer --- + +In order to use this layer, you need to make the build system aware of +it. + +Assuming the common layer exists at the top-level of your +yocto build tree, you can add it to the build system by adding the +location of the common layer to bblayers.conf, along with any +other layers needed. e.g.: + + BBLAYERS ?= " \ + /path/to/yocto/meta \ + /path/to/yocto/meta-poky \ + /path/to/yocto/meta-yocto-bsp \ + /path/to/yocto/meta-common \ + " + + +II. Misc +======== + +--- replace with specific information about the common layer --- diff --git a/meta-openbmc-mods/meta-common/classes/image_types_phosphor_auto.bbclass b/meta-openbmc-mods/meta-common/classes/image_types_phosphor_auto.bbclass new file mode 100644 index 000000000..91e699979 --- /dev/null +++ b/meta-openbmc-mods/meta-common/classes/image_types_phosphor_auto.bbclass @@ -0,0 +1,75 @@ +# Base image class extension, inlined into every image. + +# Phosphor image types +# +# New image types based on DTS partition information +# +inherit logging + +# Image composition +FLASH_FULL_IMAGE ?= "fitImage-rootfs-${MACHINE}.bin" + +IMAGE_BASETYPE ?= "squashfs-xz" +OVERLAY_BASETYPE ?= "jffs2" + +IMAGE_TYPES += "mtd-auto" + +IMAGE_TYPEDEP_mtd-auto = "${IMAGE_BASETYPE}" +IMAGE_TYPES_MASKED += "mtd-auto" + +# Flash characteristics in KB unless otherwise noted +python() { + # TODO: find partition list in DTS + DTB_FULL_FIT_IMAGE_OFFSETS = [0x80000, 0x2480000] + d.setVar('FLASH_UBOOT_OFFSET', str(0)) + if d.getVar('IMAGE_TYPE', True) == 'pfr': + d.setVar('FLASH_SIZE', str(128*1024)) + else: + d.setVar('FLASH_SIZE', str(64*1024)) + d.setVar('FLASH_RUNTIME_OFFSETS', ' '.join( + [str(int(x/1024)) for x in DTB_FULL_FIT_IMAGE_OFFSETS] + ) + ) +} + +mk_nor_image() { + image_dst="$1" + image_size_kb=$2 + dd if=/dev/zero bs=1k count=$image_size_kb \ + | tr '\000' '\377' > $image_dst +} + +do_generate_auto() { + bbdebug 1 "do_generate_auto IMAGE_TYPE=${IMAGE_TYPE} size=${FLASH_SIZE}KB (${DEPLOY_DIR_IMAGE}/${IMAGE_NAME}.auto.mtd)" + # Assemble the flash image + mk_nor_image ${IMGDEPLOYDIR}/${IMAGE_NAME}.auto.mtd ${FLASH_SIZE} + dd bs=1k conv=notrunc seek=${FLASH_UBOOT_OFFSET} \ + if=${DEPLOY_DIR_IMAGE}/u-boot.${UBOOT_SUFFIX} \ + of=${IMGDEPLOYDIR}/${IMAGE_NAME}.auto.mtd + + for OFFSET in ${FLASH_RUNTIME_OFFSETS}; do + dd bs=1k conv=notrunc seek=${OFFSET} \ + if=${DEPLOY_DIR_IMAGE}/${FLASH_FULL_IMAGE} \ + of=${IMGDEPLOYDIR}/${IMAGE_NAME}.auto.mtd + done + + ln ${IMGDEPLOYDIR}/${IMAGE_NAME}.auto.mtd \ + ${DEPLOY_DIR_IMAGE}/${IMAGE_NAME}.auto.mtd + ln -sf ${IMAGE_NAME}.auto.mtd ${DEPLOY_DIR_IMAGE}/image-mtd +} +do_generate_auto[dirs] = "${S}/auto" +do_generate_auto[depends] += " \ + ${PN}:do_image_${@d.getVar('IMAGE_BASETYPE', True).replace('-', '_')} \ + virtual/kernel:do_deploy \ + u-boot:do_populate_sysroot \ + " + +python() { + types = d.getVar('IMAGE_FSTYPES', True).split() + + if 'mtd-auto' in types: + bb.build.addtask(# task, depends_on_task, task_depends_on, d ) + 'do_generate_auto', + 'do_build', + 'do_image_complete', d) +} diff --git a/meta-openbmc-mods/meta-common/classes/obmc-phosphor-full-fitimage.bbclass b/meta-openbmc-mods/meta-common/classes/obmc-phosphor-full-fitimage.bbclass new file mode 100644 index 000000000..fefa65569 --- /dev/null +++ b/meta-openbmc-mods/meta-common/classes/obmc-phosphor-full-fitimage.bbclass @@ -0,0 +1,555 @@ +inherit uboot-sign logging + +DEPENDS += "u-boot-mkimage-native" + +SIGNING_KEY ?= "${STAGING_DIR_NATIVE}${datadir}/OpenBMC.priv" +INSECURE_KEY = "${@'${SIGNING_KEY}' == '${STAGING_DIR_NATIVE}${datadir}/OpenBMC.priv'}" +SIGNING_KEY_DEPENDS = "${@oe.utils.conditional('INSECURE_KEY', 'True', 'phosphor-insecure-signing-key-native:do_populate_sysroot', '', d)}" + +DEPS = " ${PN}:do_image_${@d.getVar('IMAGE_BASETYPE', True).replace('-', '_')} \ + virtual/kernel:do_deploy \ + u-boot:do_populate_sysroot " + +python() { + if d.getVar('IMAGE_TYPE', True) == 'pfr': + d.appendVar('DEPS', ' openssl-native:do_populate_sysroot \ + ${SIGNING_KEY_DEPENDS} \ + ${PN}:do_copy_signing_pubkey') +} + + +# Options for the device tree compiler passed to mkimage '-D' feature: +UBOOT_MKIMAGE_DTCOPTS ??= "" + +# +# Emit the fitImage ITS header +# +# $1 ... .its filename +fitimage_emit_fit_header() { + cat << EOF >> ${1} +/dts-v1/; + +/ { + description = "U-Boot fitImage for ${DISTRO_NAME}/${PV}/${MACHINE}"; + #address-cells = <1>; +EOF +} + +# +# Emit the fitImage section bits +# +# $1 ... .its filename +# $2 ... Section bit type: imagestart - image section start +# confstart - configuration section start +# sectend - section end +# fitend - fitimage end +# +fitimage_emit_section_maint() { + case $2 in + imagestart) + cat << EOF >> ${1} + + images { +EOF + ;; + confstart) + cat << EOF >> ${1} + + configurations { +EOF + ;; + sectend) + cat << EOF >> ${1} + }; +EOF + ;; + fitend) + cat << EOF >> ${1} +}; +EOF + ;; + esac +} + +# +# Emit the fitImage ITS kernel section +# +# $1 ... .its filename +# $2 ... Image counter +# $3 ... Path to kernel image +# $4 ... Compression type +# $5 ... Hash type +fitimage_emit_section_kernel() { + + kernel_csum="${5}" + + if [ -n "${kernel_csum}" ]; then + hash_blk=$(cat << EOF + hash@1 { + algo = "${kernel_csum}"; + }; +EOF + ) + fi + + ENTRYPOINT=${UBOOT_ENTRYPOINT} + if [ -n "${UBOOT_ENTRYSYMBOL}" ]; then + ENTRYPOINT=`${HOST_PREFIX}nm vmlinux | \ + awk '$3=="${UBOOT_ENTRYSYMBOL}" {print "0x"$1;exit}'` + fi + + cat << EOF >> ${1} + kernel@${2} { + description = "Linux kernel"; + data = /incbin/("${3}"); + type = "kernel"; + arch = "${UBOOT_ARCH}"; + os = "linux"; + compression = "${4}"; + load = <${UBOOT_LOADADDRESS}>; + entry = <${ENTRYPOINT}>; + ${hash_blk} + }; +EOF +} + +# +# Emit the fitImage ITS DTB section +# +# $1 ... .its filename +# $2 ... Image counter +# $3 ... Path to DTB image +# $4 ... Hash type +fitimage_emit_section_dtb() { + + dtb_csum="${4}" + if [ -n "${dtb_csum}" ]; then + hash_blk=$(cat << EOF + hash@1 { + algo = "${dtb_csum}"; + }; +EOF + ) + fi + + cat << EOF >> ${1} + fdt@${2} { + description = "Flattened Device Tree blob"; + data = /incbin/("${3}"); + type = "flat_dt"; + arch = "${UBOOT_ARCH}"; + compression = "none"; + ${hash_blk} + }; +EOF +} + +# +# Emit the fitImage ITS setup section +# +# $1 ... .its filename +# $2 ... Image counter +# $3 ... Path to setup image +# $4 ... Hash type +fitimage_emit_section_setup() { + + setup_csum="${4}" + if [ -n "${setup_csum}" ]; then + hash_blk=$(cat << EOF + hash@1 { + algo = "${setup_csum}"; + }; +EOF + ) + fi + + cat << EOF >> ${1} + setup@${2} { + description = "Linux setup.bin"; + data = /incbin/("${3}"); + type = "x86_setup"; + arch = "${UBOOT_ARCH}"; + os = "linux"; + compression = "none"; + load = <0x00090000>; + entry = <0x00090000>; + ${hash_blk} + }; +EOF +} + +# +# Emit the fitImage ITS ramdisk section +# +# $1 ... .its filename +# $2 ... Image counter +# $3 ... Path to ramdisk image +# $4 ... Hash type +fitimage_emit_section_ramdisk() { + + ramdisk_csum="${4}" + if [ -n "${ramdisk_csum}" ]; then + hash_blk=$(cat << EOF + hash@1 { + algo = "${ramdisk_csum}"; + }; +EOF + ) + fi + ramdisk_ctype="none" + ramdisk_loadline="" + ramdisk_entryline="" + + if [ -n "${UBOOT_RD_LOADADDRESS}" ]; then + ramdisk_loadline="load = <${UBOOT_RD_LOADADDRESS}>;" + fi + if [ -n "${UBOOT_RD_ENTRYPOINT}" ]; then + ramdisk_entryline="entry = <${UBOOT_RD_ENTRYPOINT}>;" + fi + + case $3 in + *.gz) + ramdisk_ctype="gzip" + ;; + *.bz2) + ramdisk_ctype="bzip2" + ;; + *.lzma) + ramdisk_ctype="lzma" + ;; + *.lzo) + ramdisk_ctype="lzo" + ;; + *.lz4) + ramdisk_ctype="lz4" + ;; + esac + + cat << EOF >> ${1} + ramdisk@${2} { + description = "${INITRAMFS_IMAGE}"; + data = /incbin/("${3}"); + type = "ramdisk"; + arch = "${UBOOT_ARCH}"; + os = "linux"; + compression = "${ramdisk_ctype}"; + ${ramdisk_loadline} + ${ramdisk_entryline} + ${hash_blk} + }; +EOF +} + +# +# Emit the fitImage ITS configuration section +# +# $1 ... .its filename +# $2 ... Linux kernel ID +# $3 ... DTB image name +# $4 ... ramdisk ID +# $5 ... config ID +# $6 ... default flag +# $7 ... Hash type +fitimage_emit_section_config() { + + conf_csum="${7}" + if [ -n "${conf_csum}" ]; then + hash_blk=$(cat << EOF + hash@1 { + algo = "${conf_csum}"; + }; +EOF + ) + fi + if [ -n "${UBOOT_SIGN_ENABLE}" ] ; then + conf_sign_keyname="${UBOOT_SIGN_KEYNAME}" + fi + + # Test if we have any DTBs at all + conf_desc="Linux kernel" + kernel_line="kernel = \"kernel@${2}\";" + fdt_line="" + ramdisk_line="" + setup_line="" + default_line="" + + if [ -n "${3}" ]; then + conf_desc="${conf_desc}, FDT blob" + fdt_line="fdt = \"fdt@${3}\";" + fi + + if [ -n "${4}" ]; then + conf_desc="${conf_desc}, ramdisk" + ramdisk_line="ramdisk = \"ramdisk@${4}\";" + fi + + if [ -n "${5}" ]; then + conf_desc="${conf_desc}, setup" + setup_line="setup = \"setup@${5}\";" + fi + + if [ "${6}" = "1" ]; then + default_line="default = \"conf@${3}\";" + fi + + cat << EOF >> ${1} + ${default_line} + conf@${3} { + description = "${6} ${conf_desc}"; + ${kernel_line} + ${fdt_line} + ${ramdisk_line} + ${setup_line} + ${hash_blk} +EOF + + if [ ! -z "${conf_sign_keyname}" ] ; then + + sign_line="sign-images = \"kernel\"" + + if [ -n "${3}" ]; then + sign_line="${sign_line}, \"fdt\"" + fi + + if [ -n "${4}" ]; then + sign_line="${sign_line}, \"ramdisk\"" + fi + + if [ -n "${5}" ]; then + sign_line="${sign_line}, \"setup\"" + fi + + sign_line="${sign_line};" + + cat << EOF >> ${1} + signature@1 { + algo = "${conf_csum},rsa2048"; + key-name-hint = "${conf_sign_keyname}"; + ${sign_line} + }; +EOF + fi + + cat << EOF >> ${1} + }; +EOF +} + +# +# Assemble fitImage +# +# $1 ... .its filename +# $2 ... fitImage name +# $3 ... include rootfs +fitimage_assemble() { + kernelcount=1 + dtbcount="" + DTBS="" + ramdiskcount=${3} + setupcount="" + #hash_type="sha256" + hash_type="" + rm -f ${1} ${2} + + # + # Step 0: find the kernel image in the deploy/images/$machine dir + # + KIMG="" + for KTYPE in zImage bzImage vmlinuz; do + if [ -e "${DEPLOY_DIR_IMAGE}/${ktype}" ]; then + KIMG="${DEPLOY_DIR_IMAGE}/${KTYPE}" + break + fi + done + if [ -z "${KIMG}" ]; then + bbdebug 1 "Failed to find kernel image to pack into full fitimage" + return 1 + fi + + fitimage_emit_fit_header ${1} + + # + # Step 1: Prepare a kernel image section. + # + fitimage_emit_section_maint ${1} imagestart + + fitimage_emit_section_kernel ${1} "${kernelcount}" "${KIMG}" "none" "${hash_type}" + + # + # Step 2: Prepare a DTB image section + # + if [ -n "${KERNEL_DEVICETREE}" ]; then + dtbcount=1 + for DTB in ${KERNEL_DEVICETREE}; do + if echo ${DTB} | grep -q '/dts/'; then + bberror "${DTB} contains the full path to the the dts file, but only the dtb name should be used." + DTB=`basename ${DTB} | sed 's,\.dts$,.dtb,g'` + fi + DTB_PATH="${DEPLOY_DIR_IMAGE}/${DTB}" + if [ ! -e "${DTB_PATH}" ]; then + bbwarn "${DTB_PATH} does not exist" + continue + fi + + DTB=$(echo "${DTB}" | tr '/' '_') + DTBS="${DTBS} ${DTB}" + fitimage_emit_section_dtb ${1} ${DTB} ${DTB_PATH} "${hash_type}" + done + fi + + # + # Step 3: Prepare a setup section. (For x86) + # + if [ -e arch/${ARCH}/boot/setup.bin ]; then + setupcount=1 + fitimage_emit_section_setup ${1} "${setupcount}" arch/${ARCH}/boot/setup.bin "${hash_type}" + fi + + # + # Step 4: Prepare a ramdisk section. + # + if [ "x${ramdiskcount}" = "x1" ] ; then + bbdebug 1 "searching for requested rootfs" + # Find and use the first initramfs image archive type we find + for img in squashfs-lz4 squashfs-xz squashfs cpio.lz4 cpio.lzo cpio.lzma cpio.xz cpio.gz cpio; do + initramfs_path="${DEPLOY_DIR_IMAGE}/${IMAGE_BASENAME}-${MACHINE}.${img}" + bbdebug 1 "looking for ${initramfs_path}" + if [ -e "${initramfs_path}" ]; then + bbdebug 1 "Found ${initramfs_path}" + fitimage_emit_section_ramdisk ${1} "${ramdiskcount}" "${initramfs_path}" "${hash_type}" + break + fi + done + fi + + fitimage_emit_section_maint ${1} sectend + + # Force the first Kernel and DTB in the default config + kernelcount=1 + if [ -n "${dtbcount}" ]; then + dtbcount=1 + fi + + # + # Step 5: Prepare a configurations section + # + fitimage_emit_section_maint ${1} confstart + + if [ -n "${DTBS}" ]; then + i=1 + for DTB in ${DTBS}; do + fitimage_emit_section_config ${1} "${kernelcount}" "${DTB}" "${ramdiskcount}" "${setupcount}" "`expr ${i} = ${dtbcount}`" "${hash_type}" + i=`expr ${i} + 1` + done + fi + + fitimage_emit_section_maint ${1} sectend + + fitimage_emit_section_maint ${1} fitend + + # + # Step 6: Assemble the image + # + uboot-mkimage \ + ${@'-D "${UBOOT_MKIMAGE_DTCOPTS}"' if len('${UBOOT_MKIMAGE_DTCOPTS}') else ''} \ + -f ${1} ${2} + + # + # Step 7: Sign the image and add public key to U-Boot dtb + # + if [ "x${UBOOT_SIGN_ENABLE}" = "x1" ] ; then + uboot-mkimage \ + ${@'-D "${UBOOT_MKIMAGE_DTCOPTS}"' if len('${UBOOT_MKIMAGE_DTCOPTS}') else ''} \ + -F -k "${UBOOT_SIGN_KEYDIR}" \ + -K "${DEPLOY_DIR_IMAGE}/${UBOOT_DTB_BINARY}" \ + -r ${2} + fi +} + +python do_generate_phosphor_manifest() { + import os.path + b = d.getVar('B', True) + manifest_filename = os.path.join(b, "MANIFEST") + version = do_get_version(d) + with open(manifest_filename, 'w') as fd: + fd.write('purpose=xyz.openbmc_project.Software.Version.VersionPurpose.BMC\n') + fd.write('version={}\n'.format(version.strip('"'))) + fd.write('KeyType={}\n'.format("OpenBMC")) + fd.write('HashType=RSA-SHA256\n') +} + +make_signatures() { + signature_files="" + for file in "$@"; do + openssl dgst -sha256 -sign ${SIGNING_KEY} -out "${file}.sig" $file + signature_files="${signature_files} ${file}.sig" + done +} + +def get_pubkey_type(d): + return os.listdir(get_pubkey_basedir(d))[0] + +def get_pubkey_path(d): + return os.path.join( + get_pubkey_basedir(d), + get_pubkey_type(d), + 'publickey') +python do_copy_signing_pubkey() { + with open(get_pubkey_path(d), 'r') as read_fd: + with open('publickey', 'w') as write_fd: + write_fd.write(read_fd.read()) +} + +do_copy_signing_pubkey[dirs] = "${S}" +do_copy_signing_pubkey[depends] += " \ + phosphor-image-signing:do_populate_sysroot \ + " + +do_image_fitimage_rootfs() { + bbdebug 1 "check for rootfs phosphor fitimage" + cd ${B} + bbdebug 1 "building rootfs phosphor fitimage" + fitimage_assemble fitImage-rootfs-${MACHINE}-${DATETIME}.its \ + fitImage-rootfs-${MACHINE}-${DATETIME}.bin 1 + + for SFX in its bin; do + SRC="fitImage-rootfs-${MACHINE}-${DATETIME}.${SFX}" + SYM="fitImage-rootfs-${MACHINE}.${SFX}" + if [ -e "${B}/${SRC}" ]; then + install -m 0644 "${B}/${SRC}" "${DEPLOY_DIR_IMAGE}/${SRC}" + ln -sf "${SRC}" "${DEPLOY_DIR_IMAGE}/${SYM}" + fi + done + ln -sf "${DEPLOY_DIR_IMAGE}/fitImage-rootfs-${MACHINE}.bin" "image-runtime" + # build a tarball with the right parts: MANIFEST, signatures, etc. + # create a directory for the tarball + mkdir -p "${B}/img" + cd "${B}/img" + # add symlinks for the contents + ln -sf "${DEPLOY_DIR_IMAGE}/u-boot.${UBOOT_SUFFIX}" "image-u-boot" + ln -sf "${DEPLOY_DIR_IMAGE}/fitImage-rootfs-${MACHINE}.bin" "image-runtime" + # add the manifest + bbdebug 1 "Manifest file: ${B}/MANIFEST" + ln -sf ${B}/MANIFEST . + # touch the required files to minimize change + touch image-kernel image-rofs image-rwfs + + if [ "${IMAGE_TYPE}" = "pfr" ]; then + ln -sf ${S}/publickey publickey + make_signatures image-u-boot image-kernel image-rofs image-rwfs image-runtime MANIFEST publickey + # tar up the update package + tar -h -cvf "${DEPLOY_DIR_IMAGE}/${PN}-image-update-${MACHINE}-${DATETIME}.tar" image-u-boot image-runtime image-kernel image-rofs image-rwfs MANIFEST publickey ${signature_files} + else + tar -h -cvf "${DEPLOY_DIR_IMAGE}/${PN}-image-update-${MACHINE}-${DATETIME}.tar" MANIFEST image-u-boot image-runtime image-kernel image-rofs image-rwfs + fi + # make a symlink + ln -sf "${PN}-image-update-${MACHINE}-${DATETIME}.tar" "${DEPLOY_DIR_IMAGE}/image-update-${MACHINE}" + ln -sf "image-update-${MACHINE}" "${DEPLOY_DIR_IMAGE}/image-update" +} + +do_image_fitimage_rootfs[vardepsexclude] = "DATETIME" +do_image_fitimage_rootfs[depends] += " ${DEPS}" + + +addtask do_image_fitimage_rootfs before do_generate_auto after do_image_complete +addtask do_generate_phosphor_manifest before do_image_fitimage_rootfs after do_image_complete diff --git a/meta-openbmc-mods/meta-common/classes/obmc-phosphor-image-common.bbclass b/meta-openbmc-mods/meta-common/classes/obmc-phosphor-image-common.bbclass new file mode 100644 index 000000000..7deca9c05 --- /dev/null +++ b/meta-openbmc-mods/meta-common/classes/obmc-phosphor-image-common.bbclass @@ -0,0 +1,36 @@ +inherit obmc-phosphor-image + +IMAGE_INSTALL_append = " \ + fan-default-speed \ + bmcweb \ + dbus-broker \ + dtc \ + dtoverlay \ + entity-manager \ + ipmitool \ + ipmi-providers \ + intel-ipmi-oem \ + phosphor-ipmi-ipmb \ + phosphor-node-manager-proxy \ + dbus-sensors \ + phosphor-webui \ + rest-dbus-static \ + nv-overlay \ + phosphor-pid-control \ + phosphor-host-postd \ + smbios-mdrv1 \ + phosphor-certificate-manager \ + set-passthrough \ + phosphor-sel-logger \ + gpiodaemon \ + smbios-mdrv2 \ + obmc-ikvm \ + system-watchdog \ + frb2-watchdog \ + srvcfg-manager \ + callback-manager \ + " + +# this package was flagged as a security risk +IMAGE_INSTALL_remove += " lrzsz" + diff --git a/meta-openbmc-mods/meta-common/classes/obmc-phosphor-image-dev.bbclass b/meta-openbmc-mods/meta-common/classes/obmc-phosphor-image-dev.bbclass new file mode 100644 index 000000000..6a1ac3f14 --- /dev/null +++ b/meta-openbmc-mods/meta-common/classes/obmc-phosphor-image-dev.bbclass @@ -0,0 +1,7 @@ +IMAGE_INSTALL_append = " \ + mtd-util \ + io-app \ + intel-fw-update \ + lpc-cmds \ + beeper-test \ + " diff --git a/meta-openbmc-mods/meta-common/conf/layer.conf b/meta-openbmc-mods/meta-common/conf/layer.conf new file mode 100644 index 000000000..7e61bfd50 --- /dev/null +++ b/meta-openbmc-mods/meta-common/conf/layer.conf @@ -0,0 +1,11 @@ +# We have a conf and classes directory, add to BBPATH +BBPATH .= ":${LAYERDIR}" + +# We have recipes-* directories, add to BBFILES +BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \ + ${LAYERDIR}/recipes-*/*/*.bbappend" + +BBFILE_COLLECTIONS += "common" +BBFILE_PATTERN_common = "^${LAYERDIR}/" +BBFILE_PRIORITY_common = "10" +LAYERSERIES_COMPAT_common = "thud" diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0001-flash-use-readX-writeX-not-udelay.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0001-flash-use-readX-writeX-not-udelay.patch new file mode 100644 index 000000000..73ab78a65 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0001-flash-use-readX-writeX-not-udelay.patch @@ -0,0 +1,424 @@ +Index: u-boot/arch/arm/mach-aspeed/flash.c +=================================================================== +--- u-boot.orig/arch/arm/mach-aspeed/flash.c ++++ u-boot/arch/arm/mach-aspeed/flash.c +@@ -28,6 +28,7 @@ + #include <common.h> + #include <asm/processor.h> + #include <asm/byteorder.h> ++#include <asm/io.h> + #include <environment.h> + + #include <asm/arch/ast_scu.h> +@@ -199,7 +200,7 @@ static void reset_flash (flash_info_t * + if (info->dualport) + ulCtrlData |= 0x08; + #endif +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + } + +@@ -228,28 +229,22 @@ static void enable_write (flash_info_t * + + ulCtrlData = (info->tCK_Write << 8); + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x06); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x06, base); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x05); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x05, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while (!(jReg & 0x02)); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + } + +@@ -280,30 +275,23 @@ static void write_status_register (flash + + ulCtrlData = (info->tCK_Write << 8); + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x01); +- udelay(10); +- *(uchar *) (base) = (uchar) (data); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x01, base); ++ writeb(data, base); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x05); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x05, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while (jReg & 0x01); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + } + + static void enable4b (flash_info_t * info) +@@ -330,13 +318,11 @@ static void enable4b (flash_info_t * inf + + ulCtrlData = (info->tCK_Write << 8); + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0xb7); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0xb7, base); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + } /* enable4b */ + +@@ -366,29 +352,23 @@ static void enable4b_spansion (flash_inf + /* Enable 4B: BAR0 D[7] = 1 */ + ulCtrlData = (info->tCK_Write << 8); + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x17); +- udelay(10); +- *(uchar *) (base) = (uchar) (0x80); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x17, base); ++ writeb(0x80, base); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x16); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x16, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while (!(jReg & 0x80)); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + } /* enable4b_spansion */ + +@@ -420,14 +400,11 @@ static void enable4b_numonyx (flash_info + /* Enable 4B: CMD:0xB7 */ + ulCtrlData = (info->tCK_Write << 8); + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0xB7); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0xB7, base); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + } /* enable4b_numonyx */ + +@@ -463,63 +440,49 @@ static void flash_write_buffer (flash_in + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x02); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x02, base); + if (info->address32) + { +- *(uchar *) (base) = (uchar) ((offset & 0xff000000) >> 24); +- udelay(10); ++ writeb((uchar) ((offset & 0xff000000) >> 24), base); + } +- *(uchar *) (base) = (uchar) ((offset & 0xff0000) >> 16); +- udelay(10); +- *(uchar *) (base) = (uchar) ((offset & 0x00ff00) >> 8); +- udelay(10); +- *(uchar *) (base) = (uchar) ((offset & 0x0000ff)); +- udelay(10); ++ writeb((uchar) ((offset & 0xff0000) >> 16), base); ++ writeb((uchar) ((offset & 0x00ff00) >> 8), base); ++ writeb((uchar) ((offset & 0x0000ff)), base); + + for (j=0; j<len; j++) + { +- *(uchar *) (base) = *(uchar *) (src++); +- udelay(10); ++ writeb(*src++, base); + } + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x05); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x05, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while ((jReg & 0x01)); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + /* RFSR */ + if (info->specificspi == SpecificSPI_N25Q512) + { + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x70); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x70, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while (!(jReg & 0x80)); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + } + } + +@@ -603,57 +566,44 @@ int flash_erase (flash_info_t * info, in + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0xd8); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0xd8, base); + if (info->address32) + { +- *(uchar *) (base) = (uchar) ((offset & 0xff000000) >> 24); +- udelay(10); ++ writeb((uchar) ((offset & 0xff000000) >> 24), base); + } +- *(uchar *) (base) = (uchar) ((offset & 0xff0000) >> 16); +- udelay(10); +- *(uchar *) (base) = (uchar) ((offset & 0x00ff00) >> 8); +- udelay(10); +- *(uchar *) (base) = (uchar) ((offset & 0x0000ff)); +- udelay(10); ++ writeb((uchar) ((offset & 0xff0000) >> 16), base); ++ writeb((uchar) ((offset & 0x00ff00) >> 8), base); ++ writeb((uchar) ((offset & 0x0000ff)), base); + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x05); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x05, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while ((jReg & 0x01)); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + + /* RFSR */ + if (info->specificspi == SpecificSPI_N25Q512) + { + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (base) = (uchar) (0x70); +- udelay(10); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x70, base); + do { +- jReg = *(volatile uchar *) (base); ++ jReg = readb(base); + } while (!(jReg & 0x80)); + ulCtrlData &= CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + } + + putc ('.'); +@@ -764,22 +714,16 @@ static ulong flash_get_size (ulong base, + } + + /* Get Flash ID */ +- ulCtrlData = *(ulong *) (info->reg_base + CtrlOffset) & CMD_MASK; ++ ulCtrlData = readl(info->reg_base + CtrlOffset) & CMD_MASK; + ulCtrlData |= CE_LOW | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); +- *(uchar *) (vbase) = (uchar) (0x9F); +- udelay(10); +- ch[0] = *(volatile uchar *)(vbase); +- udelay(10); +- ch[1] = *(volatile uchar *)(vbase); +- udelay(10); +- ch[2] = *(volatile uchar *)(vbase); +- udelay(10); +- ulCtrlData = *(ulong *) (info->reg_base + CtrlOffset) & CMD_MASK; ++ writel(ulCtrlData, info->reg_base + CtrlOffset); ++ writeb(0x9F, vbase); ++ ch[0] = readb(vbase); ++ ch[1] = readb(vbase); ++ ch[2] = readb(vbase); ++ ulCtrlData = readl(info->reg_base + CtrlOffset) & CMD_MASK; + ulCtrlData |= CE_HIGH | USERMODE; +- *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData; +- udelay(200); ++ writel(ulCtrlData, info->reg_base + CtrlOffset); + ulID = ((ulong)ch[0]) | ((ulong)ch[1] << 8) | ((ulong)ch[2] << 16) ; + info->flash_id = ulID; + +@@ -1294,13 +1238,13 @@ static ulong flash_get_size (ulong base, + + if (info->address32) { + #ifndef AST_SOC_G5 +- reg = *((volatile ulong*) 0x1e6e2070); /* set H/W Trappings */ ++ reg = readl(0x1e6e2070); /* set H/W Trappings */ + reg |= 0x10; +- *((volatile ulong*) 0x1e6e2070) = reg; ++ writel(reg, 0x1e6e2070); + #endif +- reg = *((volatile ulong*) (info->reg_base + 0x4)); /* enable 32b control bit*/ ++ reg = readl(info->reg_base + 0x4); /* enable 32b control bit*/ + reg |= (0x01 << info->CE); +- *((volatile ulong*) (info->reg_base + 0x4)) = reg; ++ writel(reg, info->reg_base + 0x4); + + /* set flash chips to 32bits addressing mode */ + if ((info->flash_id & 0xFF) == 0x01) /* Spansion */ +@@ -1322,7 +1266,7 @@ unsigned long flash_init (void) + unsigned long size = 0; + int i; + +- *((volatile ulong*) AST_FMC_BASE) |= 0x800f0000; /* enable Flash Write */ ++ writel(readl(AST_FMC_BASE) | 0x800f0000, AST_FMC_BASE); /* enable Flash Write */ + + /* Init: FMC */ + /* BANK 0 : FMC CS0 , 1: FMC CS1, */ +@@ -1352,7 +1296,7 @@ unsigned long flash_init (void) + #ifdef CONFIG_SPI0_CS + //pin switch by trap[13:12] -- [0:1] Enable SPI Master + ast_scu_spi_master(1); /* enable SPI master */ +- *((volatile ulong*) AST_FMC_SPI0_BASE) |= 0x10000; /* enable Flash Write */ ++ writel(readl(AST_FMC_SPI0_BASE) | 0x10000, AST_FMC_SPI0_BASE); /* enable Flash Write */ + flash_info[CONFIG_FMC_CS].sysspi = 1; + flash_info[CONFIG_FMC_CS].reg_base = AST_FMC_SPI0_BASE; + flash_info[CONFIG_FMC_CS].flash_id = FLASH_UNKNOWN; +@@ -1403,21 +1347,24 @@ void memmove_dma(void * dest,const void + poll_time = 100; /* set 100 us as default */ + + /* force end of burst read */ +- *(volatile ulong *) (AST_FMC_BASE + CS0_CTRL) |= CE_HIGH; +- *(volatile ulong *) (AST_FMC_BASE + CS0_CTRL) &= ~CE_HIGH; +- +- *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_CONTROL) = (ulong) (~FLASH_DMA_ENABLE); +- *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_FLASH_BASE) = (ulong) (src); +- *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_DRAM_BASE) = (ulong) (dest); +- *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_LENGTH) = (ulong) (count_align); +- *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_CONTROL) = (ulong) (FLASH_DMA_ENABLE); ++ data = readl(AST_FMC_BASE + CS0_CTRL); ++ writel(data | CE_HIGH, AST_FMC_BASE + CS0_CTRL); ++ writel(data & ~CE_HIGH, AST_FMC_BASE + CS0_CTRL); ++ ++ writel(~FLASH_DMA_ENABLE, AST_FMC_BASE + REG_FLASH_DMA_CONTROL); ++ writel((ulong)src, AST_FMC_BASE + REG_FLASH_DMA_FLASH_BASE); ++ writel((ulong)dest, AST_FMC_BASE + REG_FLASH_DMA_DRAM_BASE); ++ writel(count_align, AST_FMC_BASE + REG_FLASH_DMA_LENGTH); ++ writel(FLASH_DMA_ENABLE, AST_FMC_BASE + REG_FLASH_DMA_CONTROL); + + /* wait poll */ + do { + udelay(poll_time); +- data = *(ulong *) (AST_FMC_BASE + REG_FLASH_INTERRUPT_STATUS); ++ data = readl(AST_FMC_BASE + REG_FLASH_INTERRUPT_STATUS); + } while (!(data & FLASH_STATUS_DMA_READY)); + + /* clear status */ +- *(ulong *) (AST_FMC_BASE + REG_FLASH_INTERRUPT_STATUS) |= FLASH_STATUS_DMA_CLEAR; ++ data = readl(AST_FMC_BASE + REG_FLASH_INTERRUPT_STATUS); ++ writel(data | FLASH_STATUS_DMA_CLEAR, ++ AST_FMC_BASE + REG_FLASH_INTERRUPT_STATUS); + } diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0002-intel-layout-environment-addr.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0002-intel-layout-environment-addr.patch new file mode 100644 index 000000000..86fa5b7d5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0002-intel-layout-environment-addr.patch @@ -0,0 +1,42 @@ +Index: u-boot/include/configs/ast-common.h +=================================================================== +--- u-boot.orig/include/configs/ast-common.h ++++ u-boot/include/configs/ast-common.h +@@ -103,10 +103,13 @@ + #define CONFIG_SYS_MAX_FLASH_BANKS (CONFIG_FMC_CS) + #define CONFIG_SYS_MAX_FLASH_SECT (8192) /* max number of sectors on one chip */ + #define CONFIG_ENV_IS_IN_FLASH 1 +-#define CONFIG_ENV_ADDR (AST_FMC_CS0_BASE + 0x60000) ++#define CONFIG_ENV_OFFSET 0x2400000 /* environment starts here */ ++#define CONFIG_ENV_ADDR (AST_FMC_CS0_BASE + CONFIG_ENV_OFFSET) ++#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */ + +-#define CONFIG_ENV_OFFSET 0x60000 /* environment starts here */ +-#define CONFIG_ENV_SIZE 0x20000 /* Total Size of Environment Sector */ ++#define CONFIG_ENV_OFFSET_REDUND (CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE) ++#define CONFIG_ENV_ADDR_REDUND (AST_FMC_CS0_BASE + CONFIG_ENV_OFFSET_REDUND) ++#define CONFIG_ENV_SIZE_REDUND CONFIG_ENV_SIZE + + #define CONFIG_BOOTCOMMAND "bootm 20080000" + #define CONFIG_ENV_OVERWRITE +Index: u-boot/common/board_r.c +=================================================================== +--- u-boot.orig/common/board_r.c ++++ u-boot/common/board_r.c +@@ -494,10 +494,14 @@ static int should_load_env(void) + static int initr_env(void) + { + /* initialize environment */ +- if (should_load_env()) ++ if (should_load_env()) { ++ /* try again, in case the environment failed to load the first time */ ++ if (!gd->env_valid) ++ env_init(); + env_relocate(); +- else ++ } else { + set_default_env(NULL); ++ } + #ifdef CONFIG_OF_CONTROL + setenv_addr("fdtcontroladdr", gd->fdt_blob); + #endif diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0004-Make-sure-debug-uart-is-using-24MHz-clock-source.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0004-Make-sure-debug-uart-is-using-24MHz-clock-source.patch new file mode 100644 index 000000000..8bc0a3ed3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0004-Make-sure-debug-uart-is-using-24MHz-clock-source.patch @@ -0,0 +1,75 @@ +From 2f0e14630abec2c9679d21901072648c7802f2c4 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Tue, 11 Sep 2018 16:24:06 +0800 +Subject: [PATCH] Make sure debug uart is using 24MHz clock source + +u-boot defines the uart5(debug console) as 24MHz, +set the SCU14[28] to 0, to make sure the clock source is 24M + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + arch/arm/include/asm/arch-aspeed/ast_scu.h | 2 ++ + arch/arm/include/asm/arch-aspeed/platform.h | 1 + + arch/arm/mach-aspeed/ast-scu.c | 6 ++++++ + board/aspeed/ast-g5/ast-g5.c | 8 ++++++++ + 4 files changed, 17 insertions(+) + +diff --git a/arch/arm/include/asm/arch-aspeed/ast_scu.h b/arch/arm/include/asm/arch-aspeed/ast_scu.h +index d248416..98e6335 100644 +--- a/arch/arm/include/asm/arch-aspeed/ast_scu.h ++++ b/arch/arm/include/asm/arch-aspeed/ast_scu.h +@@ -45,4 +45,6 @@ extern void ast_scu_init_eth(u8 num); + extern void ast_scu_multi_func_eth(u8 num); + extern void ast_scu_multi_func_romcs(u8 num); + ++void ast_config_uart5_clk(void); ++ + #endif +diff --git a/arch/arm/include/asm/arch-aspeed/platform.h b/arch/arm/include/asm/arch-aspeed/platform.h +index c9c7a81..a423052 100644 +--- a/arch/arm/include/asm/arch-aspeed/platform.h ++++ b/arch/arm/include/asm/arch-aspeed/platform.h +@@ -27,6 +27,7 @@ + #include <asm/arch/ast2400_platform.h> + #elif defined(AST_SOC_G5) + #include <asm/arch/ast_g5_platform.h> ++#define CONFIG_BOARD_EARLY_INIT_F 1 /* Call board_early_init_f */ + #else + #err "No define for platform.h" + #endif +diff --git a/arch/arm/mach-aspeed/ast-scu.c b/arch/arm/mach-aspeed/ast-scu.c +index 0cc0d67..902263b 100644 +--- a/arch/arm/mach-aspeed/ast-scu.c ++++ b/arch/arm/mach-aspeed/ast-scu.c +@@ -496,3 +496,9 @@ void ast_scu_get_who_init_dram(void) + break; + } + } ++ ++void ast_config_uart5_clk(void) ++{ ++ ast_scu_write(ast_scu_read(AST_SCU_MISC2_CTRL) & ++ ~(1 << 28), AST_SCU_MISC2_CTRL); ++} +diff --git a/board/aspeed/ast-g5/ast-g5.c b/board/aspeed/ast-g5/ast-g5.c +index e67a4bf..5a1fade 100644 +--- a/board/aspeed/ast-g5/ast-g5.c ++++ b/board/aspeed/ast-g5/ast-g5.c +@@ -16,6 +16,14 @@ + + DECLARE_GLOBAL_DATA_PTR; + ++int board_early_init_f(void) ++{ ++ /* make sure uart5 is using 24MHz clock */ ++ ast_config_uart5_clk(); ++ ++ return 0; ++} ++ + int board_init(void) + { + gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0005-enable-passthrough-in-uboot.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0005-enable-passthrough-in-uboot.patch new file mode 100644 index 000000000..0385a5e31 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0005-enable-passthrough-in-uboot.patch @@ -0,0 +1,56 @@ +From b344cf4462acb1f043ed903ccee713e24ce7226d Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Wed, 7 Nov 2018 13:57:57 +0800 +Subject: [PATCH 1/1] enable passthrough in uboot + +--- + arch/arm/mach-aspeed/ast-scu.c | 22 ++++++++++++++++++++++ + board/aspeed/ast-g5/ast-g5.c | 2 ++ + 2 files changed, 24 insertions(+) + +diff --git a/arch/arm/mach-aspeed/ast-scu.c b/arch/arm/mach-aspeed/ast-scu.c +index 902263b28b..c83931ed54 100644 +--- a/arch/arm/mach-aspeed/ast-scu.c ++++ b/arch/arm/mach-aspeed/ast-scu.c +@@ -502,3 +502,25 @@ void ast_config_uart5_clk(void) + ast_scu_write(ast_scu_read(AST_SCU_MISC2_CTRL) & + ~(1 << 28), AST_SCU_MISC2_CTRL); + } ++ ++ ++void ast_enable_pass_through(void) ++{ ++ //Enable GPIOE pin mode, SCU80[16:23] = 00 */ ++ ast_scu_write(ast_scu_read(AST_SCU_FUN_PIN_CTRL1) & (~0x00FF0000), ++ AST_SCU_FUN_PIN_CTRL1); ++ ++ //Enable all pass through pins by setting SCU8C[15:12] = 0x3. ++ //Pass-through pins set: ++ //GPIOE0 -> GPIOE1 ++ //GPIOE2 -> GPIOE3 ++ ast_scu_write(ast_scu_read(AST_SCU_FUN_PIN_CTRL4) | (0x3000), ++ AST_SCU_FUN_PIN_CTRL4); ++ ++ //Disable HWStrap for GPIOE pass-through mode ++ //The write operation to SCU70(0x1e6e2070) only can set to '1'. ++ //To clear to '0', it must write '1' to 0x1e6e207c ++ if (ast_scu_read(AST_SCU_HW_STRAP1) & (0x1 << 22)){ ++ ast_scu_write((0x1 << 22), AST_SCU_REVISION_ID); ++ } ++} +diff --git a/board/aspeed/ast-g5/ast-g5.c b/board/aspeed/ast-g5/ast-g5.c +index 5a1fadeedd..b492003f51 100644 +--- a/board/aspeed/ast-g5/ast-g5.c ++++ b/board/aspeed/ast-g5/ast-g5.c +@@ -20,6 +20,8 @@ int board_early_init_f(void) + { + /* make sure uart5 is using 24MHz clock */ + ast_config_uart5_clk(); ++ /*enable pass through*/ ++ ast_enable_pass_through(); + + return 0; + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0006-Add-Aspeed-g5-interrupt-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0006-Add-Aspeed-g5-interrupt-support.patch new file mode 100644 index 000000000..dbaf7b362 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0006-Add-Aspeed-g5-interrupt-support.patch @@ -0,0 +1,389 @@ +From f33755167ddcdebbf56bc875e4091990273c6997 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Wed, 14 Nov 2018 10:21:40 -0800 +Subject: [PATCH 1/7] Add Aspeed g5 interrupt support + +This adds a few new files to the board g5 directory. Several Intel +features require interrupts running in U-Boot, so this adds basic +interrupt registration and handling support. + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +Change-Id: Id7072f1408dcf364968b1b74f2192e50a22a82f0 +--- + Kconfig | 13 ++ + arch/arm/lib/interrupts.c | 11 ++ + board/aspeed/ast-g5/Makefile | 4 +- + board/aspeed/ast-g5/ast-g5-irq.c | 176 ++++++++++++++++++++++++++++ + board/aspeed/ast-g5/ast-g5-irq.h | 39 ++++++ + board/aspeed/ast-g5/ast-g5.c | 3 + + board/aspeed/ast-g5/ast-g5.h | 7 ++ + cmd/Kconfig | 5 + + configs/ast_g5_ncsi_2boot_defconfig | 1 + + configs/ast_g5_ncsi_defconfig | 1 + + configs/ast_g5_phy_defconfig | 1 + + 11 files changed, 260 insertions(+), 1 deletion(-) + create mode 100644 board/aspeed/ast-g5/ast-g5-irq.c + create mode 100644 board/aspeed/ast-g5/ast-g5-irq.h + create mode 100644 board/aspeed/ast-g5/ast-g5.h + +diff --git a/Kconfig b/Kconfig +index 3ceff25032..d6439d01ca 100644 +--- a/Kconfig ++++ b/Kconfig +@@ -115,6 +115,19 @@ if EXPERT + When disabling this, please check if malloc calls, maybe + should be replaced by calloc - if one expects zeroed memory. + endif ++ ++config USE_IRQ ++ bool "Use interrupts" ++ default n ++ ++config STACKSIZE_IRQ ++ int "Size for IRQ stack (only if USE_IRQ enabled)" ++ default 16384 ++ ++config STACKSIZE_FIQ ++ int "Size for FIQ stack (only if USE_IRQ enabled)" ++ default 16384 ++ + endmenu # General setup + + menu "Boot images" +diff --git a/arch/arm/lib/interrupts.c b/arch/arm/lib/interrupts.c +index ed83043abb..a96b3aa070 100644 +--- a/arch/arm/lib/interrupts.c ++++ b/arch/arm/lib/interrupts.c +@@ -94,6 +94,17 @@ int disable_interrupts (void) + : "memory"); + return (old & 0x80) == 0; + } ++ ++int global_interrupts_enabled(void) ++{ ++ unsigned long old; ++ __asm__ __volatile__("mrs %0, cpsr\n" ++ : "=r" (old) ++ : ++ : "memory"); ++ return (old & 0x80) == 0; ++} ++ + #else + int interrupt_init (void) + { +diff --git a/board/aspeed/ast-g5/Makefile b/board/aspeed/ast-g5/Makefile +index d1d7f8525e..d41b11589f 100644 +--- a/board/aspeed/ast-g5/Makefile ++++ b/board/aspeed/ast-g5/Makefile +@@ -1 +1,2 @@ +-obj-y = ast-g5.o ++obj-y += ast-g5.o ++obj-y += ast-g5-irq.o +diff --git a/board/aspeed/ast-g5/ast-g5-irq.c b/board/aspeed/ast-g5/ast-g5-irq.c +new file mode 100644 +index 0000000000..860f16cf05 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-irq.c +@@ -0,0 +1,176 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include <common.h> ++#include <netdev.h> ++ ++#include <asm/arch/ast_scu.h> ++#include <asm/arch/ast-sdmc.h> ++#include <asm/io.h> ++ ++#include "ast-g5.h" ++#include "ast-g5-irq.h" ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++#ifdef CONFIG_USE_IRQ ++ ++#define VIC_STATUS_L 0x80 ++#define VIC_STATUS_H 0x84 ++#define VIC_IRQ_SELECTION_L 0x98 ++#define VIC_IRQ_SELECTION_H 0x9C ++#define VIC_ENABLE_L 0xA0 ++#define VIC_ENABLE_H 0xA4 ++#define VIC_ENABLE_CLEAR_L 0xA8 ++#define VIC_ENABLE_CLEAR_H 0xAC ++#define VIC_INTERRUPT_CLEAR_L 0xD8 ++#define VIC_INTERRUPT_CLEAR_H 0xDC ++ ++#define VIC_CLEAR_ALL (~0) ++ ++int arch_interrupt_init_early(void) ++{ ++ writel(VIC_CLEAR_ALL, AST_VIC_BASE + VIC_ENABLE_CLEAR_L); ++ writel(VIC_CLEAR_ALL, AST_VIC_BASE + VIC_ENABLE_CLEAR_H); ++ return 0; ++} ++int arch_interrupt_init(void) ++{ ++ return 0; ++} ++ ++#define AST_IRQ_START_L 0 ++#define AST_IRQ_END_L 31 ++#define AST_IRQ_START_H 32 ++#define AST_IRQ_END_H 63 ++#define AST_IRQ_COUNT 64 ++static interrupt_handler_t *handlers[AST_IRQ_COUNT] = {NULL}; ++static unsigned long irq_total = 0; ++static unsigned long irq_counts[AST_IRQ_COUNT] = {0}; ++ ++int request_irq(int irq, interrupt_handler_t *handler) ++{ ++ if (irq < AST_IRQ_START_L || irq > AST_IRQ_END_H) { ++ printf("irq %d out of range\n", irq); ++ return -1; ++ } ++ if (handlers[irq]) { ++ printf("irq %d already in use (%p)\n", irq, handlers[irq]); ++ return -1; ++ } ++ handlers[irq] = handler; ++ if (irq < AST_IRQ_START_H) { ++ writel((1 << irq), AST_VIC_BASE + VIC_ENABLE_L); ++ } else { ++ writel((1 << (irq - AST_IRQ_START_H)), ++ AST_VIC_BASE + VIC_ENABLE_H); ++ } ++ return 0; ++} ++ ++int release_irq(int irq) ++{ ++ if (irq < AST_IRQ_START_L || irq > AST_IRQ_END_H) { ++ return -1; ++ } ++ if (handlers[irq]) { ++ handlers[irq] = NULL; ++ if (irq < AST_IRQ_START_H) { ++ writel((1 << irq), AST_VIC_BASE + VIC_ENABLE_CLEAR_L); ++ } else { ++ writel((1 << (irq - AST_IRQ_START_H)), ++ AST_VIC_BASE + VIC_ENABLE_CLEAR_H); ++ } ++ } ++ return 0; ++} ++ ++extern int global_interrupts_enabled(void); ++int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int i; ++ int enabled = global_interrupts_enabled(); ++ unsigned long long irqs_enabled = ++ ((unsigned long long)readl(AST_VIC_BASE + VIC_ENABLE_H)) ++ << AST_IRQ_START_H ++ | readl(AST_VIC_BASE + VIC_ENABLE_L); ++ printf("interrupts %sabled\n", (enabled ? "en" : "dis")); ++ for (i = AST_IRQ_START_L; i < AST_IRQ_COUNT; i++) { ++ printf("% 2i (% 3s): %lu\n", i, ++ ((irqs_enabled & 1) ? "on" : "off"), irq_counts[i]); ++ irqs_enabled >>= 1; ++ } ++ printf("total: %lu\n", irq_total); ++ return 0; ++} ++ ++void do_irq(struct pt_regs *pt_regs) ++{ ++ uint32_t irq = readl(AST_VIC_BASE + VIC_STATUS_L); ++ int i; ++ irq_total++; ++ if (irq) { ++ // handler irq0-31 ++ for (i = AST_IRQ_START_L; i <= AST_IRQ_END_L; i++) { ++ if (irq & (1 << i)) { ++ irq_counts[i]++; ++ /* mask */ ++ writel((1 << i), ++ AST_VIC_BASE + VIC_ENABLE_CLEAR_L); ++ if (handlers[i]) { ++ handlers[i](pt_regs); ++ /* clear */ ++ writel((1 << i), ++ AST_VIC_BASE ++ + VIC_INTERRUPT_CLEAR_L); ++ /* unmask */ ++ writel((1 << i), ++ AST_VIC_BASE + VIC_ENABLE_L); ++ } else { ++ printf("unexpected interrupt %i; masking\n", ++ i); ++ /* clear; do not unmask */ ++ writel((1 << i), ++ AST_VIC_BASE ++ + VIC_INTERRUPT_CLEAR_L); ++ } ++ } ++ } ++ } ++ irq = readl(AST_VIC_BASE + VIC_STATUS_H); ++ if (irq) { ++ // handler irq32-63 ++ for (i = AST_IRQ_START_H; i <= AST_IRQ_END_H; i++) { ++ if (irq & (1 << (i - AST_IRQ_START_H))) { ++ irq_counts[i]++; ++ /* mask */ ++ writel((1 << (i - AST_IRQ_START_H)), ++ AST_VIC_BASE + VIC_ENABLE_CLEAR_H); ++ if (handlers[i]) { ++ handlers[i](pt_regs); ++ /* clear */ ++ writel((1 << (i - AST_IRQ_START_H)), ++ AST_VIC_BASE ++ + VIC_INTERRUPT_CLEAR_H); ++ /* unmask */ ++ writel((1 << (i - AST_IRQ_START_H)), ++ AST_VIC_BASE + VIC_ENABLE_H); ++ } else { ++ printf("unexpected interrupt %i; masking\n", ++ i); ++ /* clear; do not unmask */ ++ writel((1 << (i - AST_IRQ_START_H)), ++ AST_VIC_BASE ++ + VIC_INTERRUPT_CLEAR_H); ++ } ++ } ++ } ++ } ++} ++#endif +diff --git a/board/aspeed/ast-g5/ast-g5-irq.h b/board/aspeed/ast-g5/ast-g5-irq.h +new file mode 100644 +index 0000000000..703eeabf13 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-irq.h +@@ -0,0 +1,39 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifndef __AST_G5_IRQ_H__ ++#define __AST_G5_IRQ_H__ ++ ++#include <common.h> ++ ++#ifdef CONFIG_USE_IRQ ++ ++int arch_interrupt_init_early(void); ++ ++int request_irq(int irq, interrupt_handler_t *handler); ++ ++int release_irq(int irq); ++ ++#else /* CONFIG_USE_IRQ */ ++ ++int arch_interrupt_init_early(void) { ++ return 0; ++} ++ ++int request_irq(int irq, interrupt_handler_t *handler) { ++ return -1; ++} ++ ++int release_irq(int irq) { ++ return -1; ++} ++ ++#endif /* CONFIG_USE_IRQ */ ++ ++#endif /* __AST_G5_IRQ_H__ */ +diff --git a/board/aspeed/ast-g5/ast-g5.c b/board/aspeed/ast-g5/ast-g5.c +index b492003f51..2472aa3603 100644 +--- a/board/aspeed/ast-g5/ast-g5.c ++++ b/board/aspeed/ast-g5/ast-g5.c +@@ -14,6 +14,8 @@ + #include <asm/arch/ast-sdmc.h> + #include <asm/io.h> + ++#include "ast-g5.h" ++ + DECLARE_GLOBAL_DATA_PTR; + + int board_early_init_f(void) +@@ -22,6 +24,7 @@ int board_early_init_f(void) + ast_config_uart5_clk(); + /*enable pass through*/ + ast_enable_pass_through(); ++ arch_interrupt_init_early(); + + return 0; + } +diff --git a/board/aspeed/ast-g5/ast-g5.h b/board/aspeed/ast-g5/ast-g5.h +new file mode 100644 +index 0000000000..9fd10eccb3 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5.h +@@ -0,0 +1,7 @@ ++#ifndef _AST_G5_H_ ++#define _AST_G5_H_ ++ ++#include <common.h> ++#include "ast-g5-irq.h" ++ ++#endif /* _AST_G5_H_ */ +diff --git a/cmd/Kconfig b/cmd/Kconfig +index d69b817c82..33be2407d2 100644 +--- a/cmd/Kconfig ++++ b/cmd/Kconfig +@@ -313,6 +313,11 @@ endmenu + + menu "Device access commands" + ++config CMD_IRQ ++ bool "interrupts - enable/disable interrupts" ++ depends on USE_IRQ ++ default y ++ + config CMD_DM + bool "dm - Access to driver model information" + depends on DM +diff --git a/configs/ast_g5_ncsi_2boot_defconfig b/configs/ast_g5_ncsi_2boot_defconfig +index 2d28c86966..d5b7894a9e 100644 +--- a/configs/ast_g5_ncsi_2boot_defconfig ++++ b/configs/ast_g5_ncsi_2boot_defconfig +@@ -33,3 +33,4 @@ CONFIG_CMD_CRC32=y + CONFIG_LOOPW=y + CONFIG_CMD_MEMTEST=y + CONFIG_CMD_MX_CYCLIC=y ++CONFIG_USE_IRQ=y +diff --git a/configs/ast_g5_ncsi_defconfig b/configs/ast_g5_ncsi_defconfig +index 74029ed514..9481e5fb6e 100644 +--- a/configs/ast_g5_ncsi_defconfig ++++ b/configs/ast_g5_ncsi_defconfig +@@ -11,3 +11,4 @@ CONFIG_HUSH_PARSER=y + CONFIG_OF_LIBFDT=y + CONFIG_SPI_FLASH=y + CONFIG_SYS_NS16550=y ++CONFIG_USE_IRQ=y +diff --git a/configs/ast_g5_phy_defconfig b/configs/ast_g5_phy_defconfig +index 767f3af605..4aefcf49e8 100644 +--- a/configs/ast_g5_phy_defconfig ++++ b/configs/ast_g5_phy_defconfig +@@ -12,3 +12,4 @@ CONFIG_HUSH_PARSER=y + CONFIG_OF_LIBFDT=y + CONFIG_SPI_FLASH=y + CONFIG_SYS_NS16550=y ++CONFIG_USE_IRQ=y +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0007-Add-espi-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0007-Add-espi-support.patch new file mode 100644 index 000000000..18cc2f9c8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0007-Add-espi-support.patch @@ -0,0 +1,330 @@ +From 7ad0ea13337550f35c1e726f21d4751bf74078d6 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Wed, 14 Nov 2018 10:21:40 -0800 +Subject: [PATCH 2/7] Add espi support + +This adds basic eSPI support for U-Boot. The eSPI driver works best with +interrupts because the timing of the initialization with the PCH is not +trivial. + +The espi driver is currently just a bare-minimum driver allowing the +host to boot. In the future it may be expanded to have further +functions. + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +Change-Id: Id7072f1408dcf364968b1b74f2192e50a22a82f0 +--- + arch/arm/include/asm/arch-aspeed/regs-scu.h | 2 + + board/aspeed/ast-g5/Makefile | 1 + + board/aspeed/ast-g5/ast-g5-espi.c | 231 ++++++++++++++++++++ + board/aspeed/ast-g5/ast-g5-intel.c | 16 ++ + board/aspeed/ast-g5/ast-g5.c | 3 + + 5 files changed, 253 insertions(+) + create mode 100644 board/aspeed/ast-g5/ast-g5-espi.c + create mode 100644 board/aspeed/ast-g5/ast-g5-intel.c + +diff --git a/arch/arm/include/asm/arch-aspeed/regs-scu.h b/arch/arm/include/asm/arch-aspeed/regs-scu.h +index b714fa9234..10b983a966 100644 +--- a/arch/arm/include/asm/arch-aspeed/regs-scu.h ++++ b/arch/arm/include/asm/arch-aspeed/regs-scu.h +@@ -552,6 +552,8 @@ + + #define CLK_25M_IN (0x1 << 23) + ++#define SCU_HW_STRAP_FAST_RESET (1 << 27) ++#define SCU_HW_STRAP_ESPI_ENABLED (1 << 25) + #define SCU_HW_STRAP_2ND_BOOT_WDT (0x1 << 17) + #define SCU_HW_STRAP_SUPER_IO_CONFIG (0x1 << 16) + #define SCU_HW_STRAP_VGA_CLASS_CODE (0x1 << 15) +diff --git a/board/aspeed/ast-g5/Makefile b/board/aspeed/ast-g5/Makefile +index d41b11589f..58e0c648f4 100644 +--- a/board/aspeed/ast-g5/Makefile ++++ b/board/aspeed/ast-g5/Makefile +@@ -1,2 +1,4 @@ + obj-y += ast-g5.o ++obj-y += ast-g5-intel.o ++obj-y += ast-g5-espi.o + obj-y += ast-g5-irq.o +diff --git a/board/aspeed/ast-g5/ast-g5-espi.c b/board/aspeed/ast-g5/ast-g5-espi.c +new file mode 100644 +index 0000000000..79ef253b86 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-espi.c +@@ -0,0 +1,231 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include <common.h> ++#include <asm/io.h> ++#include <asm/arch/regs-scu.h> ++#include <asm/arch/ast_scu.h> ++#include <asm/arch/aspeed.h> ++ ++#include "ast-g5.h" ++ ++#define DEBUG_ESPI_ENABLED 1 ++#ifdef DEBUG_ESPI_ENABLED ++#define DBG_ESPI debug ++#else ++#define DBG_ESPI(...) ++#endif ++/* eSPI controller registers */ ++#define ESPI000 0x000 /* Engine Control. */ ++#define ESPI004 0x004 /* Engine Status. */ ++#define ESPI008 0x008 /* Interrupt Status. */ ++#define ESPI00C 0x00C /* Interrupt Enable. */ ++#define ESPI010 0x010 /* DMA Addr of Peripheral Channel Posted Rx pkt */ ++#define ESPI014 0x014 /* Control of Peripheral Channel Posted Rx pkt. */ ++#define ESPI018 0x018 /* Data port of Peripheral Channel Posted Rx pkt. */ ++#define ESPI020 0x020 /* DMA Addr of Peripheral Channel Posted Tx pkt. */ ++#define ESPI024 0x024 /* Control of Peripheral Channel Posted Tx pkt. */ ++#define ESPI028 0x028 /* Data port of Peripheral Channel Posted Tx pkt. */ ++#define ESPI030 0x030 /* DMA Addr of Peripheral Channel Non-Posted Tx pkt. */ ++#define ESPI034 0x034 /* Control of Peripheral Channel Non-Posted Tx pkt. */ ++#define ESPI038 0x038 /* Data port of Peripheral Channel Non-Posted Tx pkt. */ ++#define ESPI040 0x040 /* DMA Addr of OOB Channel Rx pkt. */ ++#define ESPI044 0x044 /* Control of OOB Channel Rx pkt. */ ++#define ESPI048 0x048 /* Data port of OOB Channel Rx pkt. */ ++#define ESPI050 0x050 /* DMA Addr of OOB Channel Tx pkt. */ ++#define ESPI054 0x054 /* Control of OOB Channel Tx pkt. */ ++#define ESPI058 0x058 /* Data port of OOB Channel Tx pkt. */ ++#define ESPI060 0x060 /* DMA Addr of Flash Channel Rx pkt. */ ++#define ESPI064 0x064 /* Control of Flash Channel Rx pkt. */ ++#define ESPI068 0x068 /* Data port of Flash Channel Rx pkt. */ ++#define ESPI070 0x070 /* DMA Addr of Flash Channel Tx pkt. */ ++#define ESPI074 0x074 /* Control of Flash Channel Tx pkt. */ ++#define ESPI078 0x078 /* Data port of Flash Channel Tx pkt. */ ++#define ESPI084 0x084 /* Mapping Src Addr of Peripheral Channel Rx pkt. */ ++#define ESPI088 0x088 /* Mapping Tgt Addr of Peripheral Channel Rx pkt. */ ++#define ESPI08C 0x08C /* Mapping Addr Mask of Peripheral Channel Rx pkt. */ ++#define ESPI090 0x090 /* Mapping Target Addr and Mask of Flash Channel. */ ++#define ESPI094 0x094 /* Interrupt enable of System Event from Master. */ ++#define ESPI098 0x098 /* System Event from and to Master. */ ++#define ESPI09C 0x09C /* GPIO through Virtual Wire Channel. */ ++#define ESPI0A0 0x0A0 /* General Capabilities and Configurations. */ ++#define ESPI0A4 0x0A4 /* Channel 0 Capabilities and Configurations. */ ++#define ESPI0A8 0x0A8 /* Channel 1 Capabilities and Configurations. */ ++#define ESPI0AC 0x0AC /* Channel 2 Capabilities and Configurations. */ ++#define ESPI0B0 0x0B0 /* Channel 3 Capabilities and Configurations. */ ++#define ESPI0B4 0x0B4 /* GPIO Direction of Virtual Wire Channel. */ ++#define ESPI0B8 0x0B8 /* GPIO Selection of Virtual Wire Channel. */ ++#define ESPI0BC 0x0BC /* GPIO Reset Selection of Virtual Wire Channel. */ ++#define ESPI100 0x100 /* Interrupt enable of System Event 1 from Master. */ ++#define ESPI104 0x104 /* System Event 1 from and to Master. */ ++#define ESPI110 0x110 /* Interrupt type 0 of System Event from Master. */ ++#define ESPI114 0x114 /* Interrupt type 1 of System Event from Master. */ ++#define ESPI118 0x118 /* Interrupt type 2 of System Event from Master. */ ++#define ESPI11C 0x11C /* Interrupt status of System Event from Master. */ ++#define ESPI120 0x120 /* Interrupt type 0 of System Event 1 from Master. */ ++#define ESPI124 0x124 /* Interrupt type 1 of System Event 1 from Master. */ ++#define ESPI128 0x128 /* Interrupt type 2 of System Event 1 from Master. */ ++#define ESPI12C 0x12C /* Interrupt status of System Event 1 from Master. */ ++#define ESPICFG004 0x004 /* Device Identification. */ ++#define ESPICFG008 0x008 /* General Capabilities and Configurations. */ ++#define ESPICFG010 0x010 /* Channel 0 Capabilities and Configurations. */ ++#define ESPICFG020 0x020 /* Channel 1 Capabilities and Configurations. */ ++#define ESPICFG030 0x030 /* Channel 2 Capabilities and Configurations. */ ++#define ESPICFG040 0x040 /* Channel 3 Capabilities and Configurations. */ ++#define ESPICFG044 0x044 /* Channel 3 Capabilities and Configurations 2. */ ++#define ESPICFG800 0x800 /* GPIO Direction of Virtual Wire Channel. */ ++#define ESPICFG804 0x804 /* GPIO Selection of Virtual Wire Channel. */ ++#define ESPICFG808 0x808 /* GPIO Reset Selection of Virtual Wire Channel. */ ++#define ESPICFG810 0x810 /* Mapping Src Addr of Peripheral Channel Rx pkt */ ++#define ESPICFG814 0x814 /* Mapping Tgt Addr of Peripheral Channel Rx pkt */ ++#define ESPICFG818 0x818 /* Mapping Addr Mask of Peripheral Channel Rx pkt */ ++ ++/* ESPI000 bits */ ++#define AST_ESPI_OOB_CHRDY (1 << 4) ++#define AST_ESPI_FLASH_SW_CHRDY (0x1 << 7) ++#define AST_ESPI_FLASH_SW_READ (0x1 << 10) ++ ++/* ESPI00C bits (Interrupt Enable) */ ++#define AST_ESPI_IEN_SYS_EV (1 << 8) ++#define AST_ESPI_IEN_GPIO_EV (1 << 9) ++ ++/* ESPI008 bits ISR */ ++#define AST_ESPI_VW_SYS_EVT (1 << 8) ++#define AST_ESPI_VW_SYS_EV1 (1 << 22) ++ ++/* ESPI098 and ESPI11C bits */ ++#define AST_ESPI_OOB_RST_WARN (1 << 6) ++#define AST_ESPI_HOST_RST_WARN (1 << 8) ++#define AST_ESPI_OOB_RST_ACK (1 << 16) ++#define AST_ESPI_SL_BT_DONE (1 << 20) ++#define AST_ESPI_SL_BT_STATUS (1 << 23) ++#define AST_ESPI_HOST_RST_ACK (1 << 27) ++ ++/* ESPI104 bits */ ++#define AST_ESPI_SUS_WARN (1 << 0) ++#define AST_ESPI_SUS_ACK (1 << 20) ++ ++/* LPC chip ID */ ++#define SCR0SIO 0x170 ++#define IRQ_SRC_ESPI 23 /* IRQ 23 */ ++ ++static int espi_irq_handler(struct pt_regs *regs) ++{ ++ uint32_t irq_status = readl(AST_ESPI_BASE + ESPI008); ++ ++ DBG_ESPI("ISR irq_status : 0x%08X\n", irq_status); ++ ++ if (irq_status & AST_ESPI_VW_SYS_EVT) { ++ uint32_t sys_status = readl(AST_ESPI_BASE + ESPI11C); ++ uint32_t sys_event = readl(AST_ESPI_BASE + ESPI098); ++ ++ DBG_ESPI("sys_status : 0x%08X\n", sys_status); ++ if (sys_status & AST_ESPI_HOST_RST_WARN) { ++ DBG_ESPI("HOST_RST_WARN ev: %08X\n", sys_event); ++ if (sys_event & AST_ESPI_HOST_RST_WARN) { ++ uint32_t v = readl(AST_ESPI_BASE + ESPI098) ++ | AST_ESPI_HOST_RST_ACK; ++ writel(v, AST_ESPI_BASE + ESPI098); ++ } ++ } ++ if (sys_status & AST_ESPI_OOB_RST_WARN) { ++ DBG_ESPI("OOB_RST_WARN ev: %08X\n", sys_event); ++ if (sys_event & AST_ESPI_OOB_RST_WARN) { ++ uint32_t v = readl(AST_ESPI_BASE + ESPI098) ++ | AST_ESPI_OOB_RST_ACK; ++ writel(v, AST_ESPI_BASE + ESPI098); ++ } ++ } ++ writel(sys_status, AST_ESPI_BASE + ESPI11C); // clear status ++ } ++ ++ if (irq_status & AST_ESPI_VW_SYS_EV1) { ++ uint32_t sys1_status = readl(AST_ESPI_BASE + ESPI12C); ++ uint32_t sys1_event = readl(AST_ESPI_BASE + ESPI104); ++ ++ DBG_ESPI("sys1_status : 0x%08X\n", sys1_status); ++ if (sys1_status & AST_ESPI_SUS_WARN) { ++ DBG_ESPI("SUS WARN ev: %08X\n", sys1_event); ++ if (sys1_event & AST_ESPI_SUS_WARN) { ++ uint32_t v = readl(AST_ESPI_BASE + ESPI104) ++ | AST_ESPI_SUS_ACK; ++ writel(v, AST_ESPI_BASE + ESPI104); ++ } ++ } ++ writel(sys1_status, AST_ESPI_BASE + ESPI12C); // clear status ++ } ++ writel(irq_status, AST_ESPI_BASE + ESPI008); // clear irq_status ++ return 0; ++} ++ ++static void espi_handshake_ack(void) ++{ ++ // IRQ only serviced if strapped, so no strap check ++ if (!(readl(AST_ESPI_BASE + ESPI098) & AST_ESPI_SL_BT_STATUS)) { ++ DBG_ESPI("Setting espi slave boot done\n"); ++ uint32_t v = readl(AST_ESPI_BASE + ESPI098) ++ | AST_ESPI_SL_BT_STATUS | AST_ESPI_SL_BT_DONE; ++ writel(v, AST_ESPI_BASE + ESPI098); ++ } ++ ++ if (readl(AST_ESPI_BASE + ESPI104) & AST_ESPI_SUS_WARN) { ++ DBG_ESPI("Boot SUS WARN set %08x\n", ++ readl(AST_ESPI_BASE + ESPI104)); ++ uint32_t v = readl(AST_ESPI_BASE + ESPI104) | AST_ESPI_SUS_ACK; ++ writel(v, AST_ESPI_BASE + ESPI104); ++ } ++} ++ ++void espi_init(void) ++{ ++ if (readl(AST_SCU_BASE + AST_SCU_HW_STRAP1) ++ & SCU_HW_STRAP_ESPI_ENABLED) { ++ uint32_t v; ++ DBG_ESPI("espi_init\n"); ++ ++ /* Block flash access from Host */ ++ v = readl(AST_ESPI_BASE + ESPI000) & ~AST_ESPI_FLASH_SW_CHRDY; ++ v |= AST_ESPI_FLASH_SW_READ | AST_ESPI_OOB_CHRDY; ++ writel(v, AST_ESPI_BASE + ESPI000); ++ ++ /* Set SIO register 0x28 to 0xa8 as a faked ASPEED ChipID for ++ * BIOS using in eSPI mode */ ++ v = readl(AST_LPC_BASE + SCR0SIO) & ~0x000000ff; ++ writel(v, AST_LPC_BASE + SCR0SIO); ++ v = readl(AST_LPC_BASE + SCR0SIO) | 0xa8; ++ writel(v, AST_LPC_BASE + SCR0SIO); ++ ++ v = readl(AST_ESPI_BASE + ESPI000) | AST_ESPI_OOB_CHRDY; ++ writel(v, AST_ESPI_BASE + ESPI000); ++ ++ writel(0, AST_ESPI_BASE + ESPI110); ++ writel(0, AST_ESPI_BASE + ESPI114); ++ writel(AST_ESPI_HOST_RST_WARN | AST_ESPI_OOB_RST_WARN, ++ AST_ESPI_BASE + ESPI118); ++ writel(AST_ESPI_HOST_RST_WARN | AST_ESPI_OOB_RST_WARN, ++ AST_ESPI_BASE + ESPI094); ++ ++ writel(AST_ESPI_SUS_WARN, ++ AST_ESPI_BASE + ESPI120); // int type 0 susp warn ++ writel(0, AST_ESPI_BASE + ESPI124); ++ writel(0, AST_ESPI_BASE + ESPI128); ++ writel(AST_ESPI_SUS_WARN, ++ AST_ESPI_BASE ++ + ESPI100); // Enable sysev1 ints for susp warn ++ ++ writel(AST_ESPI_IEN_SYS_EV, ++ AST_ESPI_BASE + ESPI00C); // Enable only sys events ++ ++ espi_handshake_ack(); ++ ++ request_irq(IRQ_SRC_ESPI, espi_irq_handler); ++ } else { ++ DBG_ESPI("No espi strap\n"); ++ } ++} +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +new file mode 100644 +index 0000000000..e79235c8d0 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -0,0 +1,16 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include <common.h> ++ ++extern void espi_init(void); ++void ast_g5_intel(void) ++{ ++ espi_init(); ++} +diff --git a/board/aspeed/ast-g5/ast-g5.c b/board/aspeed/ast-g5/ast-g5.c +index 2472aa3603..d41ef9cbd3 100644 +--- a/board/aspeed/ast-g5/ast-g5.c ++++ b/board/aspeed/ast-g5/ast-g5.c +@@ -18,6 +18,8 @@ + + DECLARE_GLOBAL_DATA_PTR; + ++extern void ast_g5_intel(void); ++ + int board_early_init_f(void) + { + /* make sure uart5 is using 24MHz clock */ +@@ -34,6 +36,7 @@ int board_init(void) + gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100; + gd->flags = 0; + ++ ast_g5_intel(); + return 0; + } + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0008-add-sgio-support-for-port80-snoop-post-LEDs.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0008-add-sgio-support-for-port80-snoop-post-LEDs.patch new file mode 100644 index 000000000..cb61bd545 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0008-add-sgio-support-for-port80-snoop-post-LEDs.patch @@ -0,0 +1,160 @@ +From cf43453a75880cf53ea7bbf5859706f2a7cae292 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Wed, 14 Nov 2018 12:09:52 -0800 +Subject: [PATCH 3/7] add sgio support for port80 snoop post LEDs + +This ties together the port 80 snooping to the SGPIO output that +ultimately drives the POST code LEDs. + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +Change-Id: Iaa1b91cd40f4b6323dba0598da373cb631459e66 +--- + arch/arm/include/asm/arch-aspeed/ast_scu.h | 1 + + arch/arm/mach-aspeed/ast-scu.c | 8 ++ + board/aspeed/ast-g5/ast-g5-intel.c | 96 ++++++++++++++++++++++ + 3 files changed, 105 insertions(+) + +diff --git a/arch/arm/include/asm/arch-aspeed/ast_scu.h b/arch/arm/include/asm/arch-aspeed/ast_scu.h +index 98e63351f1..c10e6a9d4b 100644 +--- a/arch/arm/include/asm/arch-aspeed/ast_scu.h ++++ b/arch/arm/include/asm/arch-aspeed/ast_scu.h +@@ -44,6 +44,7 @@ extern u32 ast_scu_get_vga_memsize(void); + extern void ast_scu_init_eth(u8 num); + extern void ast_scu_multi_func_eth(u8 num); + extern void ast_scu_multi_func_romcs(u8 num); ++extern void ast_scu_multi_func_sgpio(void); + + void ast_config_uart5_clk(void); + +diff --git a/arch/arm/mach-aspeed/ast-scu.c b/arch/arm/mach-aspeed/ast-scu.c +index c83931ed54..63e9c7c167 100644 +--- a/arch/arm/mach-aspeed/ast-scu.c ++++ b/arch/arm/mach-aspeed/ast-scu.c +@@ -407,6 +407,14 @@ void ast_scu_multi_func_romcs(u8 num) + SCU_FUN_PIN_ROMCS(num), AST_SCU_FUN_PIN_CTRL3); + } + ++void ast_scu_multi_func_sgpio(void) ++{ ++ ast_scu_write(ast_scu_read(AST_SCU_FUN_PIN_CTRL2) | ++ SCU_FUN_PIN_SGPMI | SCU_FUN_PIN_SGPMO | ++ SCU_FUN_PIN_SGPMLD | SCU_FUN_PIN_SGPMCK, ++ AST_SCU_FUN_PIN_CTRL2); ++} ++ + u32 ast_scu_revision_id(void) + { + int i; +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index e79235c8d0..fca4d91115 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -8,9 +8,105 @@ + */ + + #include <common.h> ++#include <asm/io.h> ++#include <asm/arch/regs-scu.h> ++#include <asm/arch/ast_scu.h> ++#include <asm/arch/aspeed.h> ++ ++#include "ast-g5.h" ++ ++#define LPC_SNOOP_ADDR 0x80 ++#define HICR5 0x080 /* Host Interface Control Register 5 */ ++#define HICR6 0x084 /* Host Interface Control Register 6 */ ++#define HICR7 0x088 /* Host Interface Control Register 7 */ ++#define HICR8 0x08c /* Host Interface Control Register 8 */ ++#define SNPWADR 0x090 /* LPC Snoop Address Register */ ++#define SNPWDR 0x094 /* LPC Snoop Data Register */ ++#define HICR9 0x098 /* Host Interface Control Register 9 */ ++#define HICRA 0x09c /* Host Interface Control Register A */ ++#define LHCR0 0x0a0 /* LPC Host Control Register 0 */ ++#define LHCR1 0x0a4 /* LPC Host Control Register 1 */ ++#define LHCR2 0x0a8 /* LPC Host Control Register 2 */ ++#define LHCR3 0x0ac /* LPC Host Control Register 3 */ ++#define LHCR4 0x0b0 /* LPC Host Control Register 4 */ ++#define LHCR5 0x0b4 /* LPC Host Control Register 5 */ ++#define LHCR6 0x0b8 /* LPC Host Control Register 6 */ ++#define LHCR7 0x0bc /* LPC Host Control Register 7 */ ++#define LHCR8 0x0c0 /* LPC Host Control Register 8 */ ++#define PCCR6 0x0c4 /* Post Code Control Register 6 */ ++#define LHCRA 0x0c8 /* LPC Host Control Register A */ ++#define LHCRB 0x0cc /* LPC Host Control Register B */ ++#define PCCR4 0x0d0 /* Post Code Control Register 4 */ ++#define PCCR5 0x0d4 /* Post Code Control Register 5 */ ++#define HICRB 0x100 /* Host Interface Control Register B */ ++#define HICRC 0x104 /* Host Interface Control Register C */ ++/* HICR5 Bits */ ++#define HICR5_EN_SIOGIO (1 << 31) /* Enable SIOGIO */ ++#define HICR5_EN80HGIO (1 << 30) /* Enable 80hGIO */ ++#define HICR5_SEL80HGIO (0x1f << 24) /* Select 80hGIO */ ++#define SET_SEL80HGIO(x) ((x & 0x1f) << 24) /* Select 80hGIO Offset */ ++#define HICR5_UNKVAL_MASK 0x1FFF0000 /* Bits with unknown values on reset */ ++#define HICR5_ENINT_SNP0W (1 << 1) /* Enable Snooping address 0 */ ++#define HICR5_EN_SNP0W (1 << 0) /* Enable Snooping address 0 */ ++ ++/* HRCR6 Bits */ ++#define HICR6_STR_SNP0W (1 << 0) /* Interrupt Status Snoop address 0 */ ++#define HICR6_STR_SNP1W (1 << 1) /* Interrupt Status Snoop address 1 */ ++ ++/* HICRB Bits */ ++#define HICRB_EN80HSGIO (1 << 13) /* Enable 80hSGIO */ ++ ++#define SGPIO_CLK_DIV(N) ((N) << 16) ++#define SGPIO_BYTES(N) ((N) << 6) ++#define SGPIO_ENABLE 1 ++#define GPIO254 0x254 ++ ++static void sgpio_init(void) ++{ ++ uint32_t value; ++ /* ++ 33.4.2 ++ LPC port80h direct to SGPIO ++ In AST2500 SGPIO, it supports output data from 80h. It always uses SGPIOA. ++ 1. Configure LPC snoop function. ++ (a) Set SNPWADR(0x1e789090)[15:0] to 0x80. ++ (b) Set HICR5(0x1e789080)[0] to 1 to enable snoop. ++ 2. Configure SGPIO ++ (a) Set GPIO254[9:6] to larger than or equal to 0x1. ++ (b) Set GPIO254[0] to 1 to enable SGPIO. ++ 3. Set SuperIO ++ (a) Set SIOR7 30h to 0x40. ++ */ ++ /* make sure multi-pin stuff goes in our favor */ ++ ast_scu_multi_func_sgpio(); ++ ++ /* set lpc snoop #0 to port 0x80 */ ++ value = readl(AST_LPC_BASE + SNPWADR) & 0xffff0000; ++ writel(value | LPC_SNOOP_ADDR, AST_LPC_BASE + SNPWADR); ++ ++ /* clear interrupt status */ ++ value = readl(AST_LPC_BASE + HICR6); ++ value |= HICR6_STR_SNP0W | HICR6_STR_SNP1W; ++ writel(value, AST_LPC_BASE + HICR6); ++ ++ /* enable lpc snoop #0 and SIOGIO */ ++ value = readl(AST_LPC_BASE + HICR5) & ~(HICR5_UNKVAL_MASK); ++ value |= HICR5_EN_SIOGIO | HICR5_EN_SNP0W | HICR5_ENINT_SNP0W; ++ writel(value, AST_LPC_BASE + HICR5); ++ ++ ++ /* enable port80h snoop on SGPIO */ ++ value = readl(AST_LPC_BASE + HICRB) | HICRB_EN80HSGIO; ++ writel(value, AST_LPC_BASE + HICRB); ++ ++ /* set the gpio clock to pclk/(2*(5+1)) or ~2 MHz */ ++ value = SGPIO_CLK_DIV(256) | SGPIO_BYTES(10) | SGPIO_ENABLE; ++ writel(value, AST_GPIO_BASE + GPIO254); ++} + + extern void espi_init(void); + void ast_g5_intel(void) + { + espi_init(); ++ sgpio_init(); + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0009-Add-basic-GPIO-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0009-Add-basic-GPIO-support.patch new file mode 100644 index 000000000..a49f196ac --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0009-Add-basic-GPIO-support.patch @@ -0,0 +1,414 @@ +From 89728d8c255204c8d9ec46a1dc0d412b04708f22 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Fri, 16 Nov 2018 09:58:01 -0800 +Subject: [PATCH 4/7] Add basic GPIO support + +Add a table of well-known gpios (such as FP LEDs and FF UPD jumper) and +initialize them at boot. + +Add a mechanism to get/set well known gpios from command line. + +Change-Id: I4136a5ccb048b3604f13b17ea0c18a4bc596c249 +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + board/aspeed/ast-g5/Makefile | 1 + + board/aspeed/ast-g5/ast-g5-gpio.c | 195 +++++++++++++++++++++++++++++ + board/aspeed/ast-g5/ast-g5-gpio.h | 102 +++++++++++++++ + board/aspeed/ast-g5/ast-g5-intel.c | 42 +++++++ + board/aspeed/ast-g5/ast-g5.h | 1 + + 5 files changed, 341 insertions(+) + create mode 100644 board/aspeed/ast-g5/ast-g5-gpio.c + create mode 100644 board/aspeed/ast-g5/ast-g5-gpio.h + +diff --git a/board/aspeed/ast-g5/Makefile b/board/aspeed/ast-g5/Makefile +index 58e0c648f4..2970ae5741 100644 +--- a/board/aspeed/ast-g5/Makefile ++++ b/board/aspeed/ast-g5/Makefile +@@ -2,3 +2,4 @@ obj-y += ast-g5.o + obj-y += ast-g5-intel.o + obj-y += ast-g5-espi.o + obj-y += ast-g5-irq.o ++obj-y += ast-g5-gpio.o +diff --git a/board/aspeed/ast-g5/ast-g5-gpio.c b/board/aspeed/ast-g5/ast-g5-gpio.c +new file mode 100644 +index 0000000000..d596c15914 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-gpio.c +@@ -0,0 +1,195 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include <common.h> ++#include <asm/io.h> ++#include <asm/arch/regs-scu.h> ++#include <asm/arch/ast_scu.h> ++#include <asm/arch/aspeed.h> ++ ++#include "ast-g5.h" ++#include "ast-g5-gpio.h" ++ ++typedef struct _gpio_bases { ++ uint32_t u32ddr; /* data and direction registers */ ++ uint32_t u32intcfg; /* interrupt config */ ++ uint32_t u32debounce; /* debounce config */ ++ uint32_t u32cmdsrc; /* command source config */ ++} sGPIO_BASES; ++ ++static const sGPIO_BASES GPIO_BASES[] = { ++ /* ABCD */ ++ {AST_GPIO_BASE + 0x0000, AST_GPIO_BASE + 0x0008, ++ AST_GPIO_BASE + 0x0040, AST_GPIO_BASE + 0x0060}, ++ /* EFGH */ ++ {AST_GPIO_BASE + 0x0020, AST_GPIO_BASE + 0x0028, ++ AST_GPIO_BASE + 0x0048, AST_GPIO_BASE + 0x0068}, ++ /* IJKL */ ++ {AST_GPIO_BASE + 0x0070, AST_GPIO_BASE + 0x0098, ++ AST_GPIO_BASE + 0x00b0, AST_GPIO_BASE + 0x0090}, ++ /* MNOP */ ++ {AST_GPIO_BASE + 0x0078, AST_GPIO_BASE + 0x00e8, ++ AST_GPIO_BASE + 0x0100, AST_GPIO_BASE + 0x00e0}, ++ /* QRST */ ++ {AST_GPIO_BASE + 0x0080, AST_GPIO_BASE + 0x0118, ++ AST_GPIO_BASE + 0x0130, AST_GPIO_BASE + 0x0110}, ++ /* UVWX */ ++ {AST_GPIO_BASE + 0x0088, AST_GPIO_BASE + 0x0148, ++ AST_GPIO_BASE + 0x0160, AST_GPIO_BASE + 0x0140}, ++ /* YZAB */ ++ {AST_GPIO_BASE + 0x01e0, AST_GPIO_BASE + 0x0178, ++ AST_GPIO_BASE + 0x0190, AST_GPIO_BASE + 0x0170}, ++ /* AC__ */ ++ {AST_GPIO_BASE + 0x01e8, AST_GPIO_BASE + 0x01a8, ++ AST_GPIO_BASE + 0x01c0, AST_GPIO_BASE + 0x01a0}, ++}; ++ ++static size_t gpio_max = 0; ++static const GPIOValue * gpio_table = NULL; ++ ++void gpio_set_value(int n, int asserted) ++{ ++ uint8_t port; ++ uint8_t pin; ++ uint32_t base; ++ uint8_t shift; ++ uint8_t assert; ++ uint32_t gpio_value; ++ ++ if (n >= gpio_max || !gpio_table) { ++ return; ++ } ++ port = GPIO_PORT(gpio_table[n].u8PortPin); ++ assert = GPIO_PORT(gpio_table[n].u8Value); ++ pin = GPIO_PIN(gpio_table[n].u8PortPin); ++ base = GPIO_BASES[GPIO_GROUP(port)].u32ddr; ++ shift = GPIO_SHIFT(port, pin); ++ ++ gpio_value = readl(base + GPIO_DATA_VALUE); ++ if ((assert &&asserted) || !(assert || asserted)) { ++ // set the bit ++ gpio_value |= (1 << shift); ++ } else { ++ // clear the bit ++ gpio_value &= ~(1 << shift); ++ } ++ writel(gpio_value, base + GPIO_DATA_VALUE); ++} ++ ++int gpio_get_value(int n) ++{ ++ uint8_t port; ++ uint8_t pin; ++ uint32_t base; ++ uint8_t shift; ++ uint8_t assert; ++ uint32_t gpio_value; ++ ++ if (n >= gpio_max || !gpio_table) { ++ return -1; ++ } ++ port = GPIO_PORT(gpio_table[n].u8PortPin); ++ assert = GPIO_PORT(gpio_table[n].u8Value); ++ pin = GPIO_PIN(gpio_table[n].u8PortPin); ++ base = GPIO_BASES[GPIO_GROUP(port)].u32ddr; ++ shift = GPIO_SHIFT(port, pin); ++ ++ gpio_value = readl(base + GPIO_DATA_VALUE); ++ gpio_value >>= shift; ++ gpio_value &= 1; ++ gpio_value ^= assert; ++ return !gpio_value; ++} ++ ++void gpio_init(const GPIOValue* table, size_t count) ++{ ++ uint32_t pclk, value; ++ int i; ++ ++ gpio_table = table; ++ gpio_max = count; ++ /* set up the debounce timers (in units of PCLK cycles) */ ++ pclk = ast_get_ahbclk(); ++ /* GPIO_DEBOUNCE_120us */ ++ writel((pclk / 1000000) * 120, AST_GPIO_BASE + GPIO_DEBOUNCE_TIMER_0); ++ /* GPIO_DEBOUNCE_8ms */ ++ writel((pclk / 1000000) * 8000, AST_GPIO_BASE + GPIO_DEBOUNCE_TIMER_1); ++ /* GPIO_DEBOUNCE_16ms */ ++ writel((pclk / 1000000) * 16000, AST_GPIO_BASE + GPIO_DEBOUNCE_TIMER_2); ++ ++ for (i = 0; i < gpio_max; i++) { ++ uint8_t port; ++ uint8_t pin; ++ uint32_t base; ++ uint8_t shift; ++ ++ port = GPIO_PORT(gpio_table[i].u8PortPin); ++ pin = GPIO_PIN(gpio_table[i].u8PortPin); ++ base = GPIO_BASES[GPIO_GROUP(port)].u32ddr; ++ shift = GPIO_SHIFT(port, pin); ++ ++ /* set direction */ ++ value = readl(base + GPIO_DIRECTION); ++ if (gpio_table[i].u8PinCFG & GPCFG_OUTPUT_EN) ++ value |= (1 << shift); ++ else ++ value &= ~(1 << shift); ++ writel(value, base + GPIO_DIRECTION); ++ ++ /* set data value */ ++ value = readl(base + GPIO_DATA_VALUE); ++ if (gpio_table[i].u8Value) ++ value |= (1 << shift); ++ else ++ value &= ~(1 << shift); ++ writel(value, base + GPIO_DATA_VALUE); ++ ++ /* set debounce */ ++ base = GPIO_BASES[GPIO_GROUP(port)].u32debounce; ++ value = readl(base + GPIO_DEBOUNCE_SEL_0); ++ if (gpio_table[i].u8Debounce & 0x01) ++ value |= (1 << shift); ++ else ++ value &= ~(1 << shift); ++ writel(value, base + GPIO_DEBOUNCE_SEL_0); ++ value = readl(base + GPIO_DEBOUNCE_SEL_1); ++ if (gpio_table[i].u8Debounce & 0x02) ++ value |= (1 << shift); ++ else ++ value &= ~(1 << shift); ++ writel(value, base + GPIO_DEBOUNCE_SEL_1); ++ } ++} ++ ++int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int n; ++ if (argc < 3) { ++ return 1; ++ } ++ n = simple_strtoul(argv[2], NULL, 16); ++ if (argv[1][0] == 'g') { ++ printf("%d\n", gpio_get_value(n)); ++ return 0; ++ } ++ if (argc < 4) { ++ return 1; ++ } ++ if (argv[1][0] == 's') { ++ int value; ++ value = simple_strtoul(argv[3], NULL, 16); ++ gpio_set_value(n, value); ++ return 0; ++ } ++ ++ return 1; ++} ++U_BOOT_CMD(gpio, 4, 0, do_gpio, ++ "do stuff with gpios <set|get> [n] [value]", ++ ""); +diff --git a/board/aspeed/ast-g5/ast-g5-gpio.h b/board/aspeed/ast-g5/ast-g5-gpio.h +new file mode 100644 +index 0000000000..a820c0fcad +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-gpio.h +@@ -0,0 +1,102 @@ ++#ifndef __HW_GPIO_H__ ++#define __HW_GPIO_H__ ++ ++#define GPIO_PORT_A 0 ++#define GPIO_PORT_B 1 ++#define GPIO_PORT_C 2 ++#define GPIO_PORT_D 3 ++#define GPIO_PORT_E 4 ++#define GPIO_PORT_F 5 ++#define GPIO_PORT_G 6 ++#define GPIO_PORT_H 7 ++#define GPIO_PORT_I 8 ++#define GPIO_PORT_J 9 ++#define GPIO_PORT_K 10 ++#define GPIO_PORT_L 11 ++#define GPIO_PORT_M 12 ++#define GPIO_PORT_N 13 ++#define GPIO_PORT_O 14 ++#define GPIO_PORT_P 15 ++#define GPIO_PORT_Q 16 ++#define GPIO_PORT_R 17 ++#define GPIO_PORT_S 18 ++#define GPIO_PORT_T 19 ++#define GPIO_PORT_U 20 ++#define GPIO_PORT_V 21 ++#define GPIO_PORT_W 22 ++#define GPIO_PORT_X 23 ++#define GPIO_PORT_Y 24 ++#define GPIO_PORT_Z 25 ++#define GPIO_PORT_AA 26 ++#define GPIO_PORT_AB 27 ++#define GPIO_PORT_AC 28 ++ ++#define GPIO_PIN_0 0 ++#define GPIO_PIN_1 1 ++#define GPIO_PIN_2 2 ++#define GPIO_PIN_3 3 ++#define GPIO_PIN_4 4 ++#define GPIO_PIN_5 5 ++#define GPIO_PIN_6 6 ++#define GPIO_PIN_7 7 ++ ++#define GPIO_DEBOUNCE_TIMER_0 0x50 ++#define GPIO_DEBOUNCE_TIMER_1 0x54 ++#define GPIO_DEBOUNCE_TIMER_2 0x58 ++ ++/* relative to u32ddr base */ ++#define GPIO_DATA_VALUE 0x00 ++#define GPIO_DIRECTION 0x04 ++/* relative to u32intcfg base */ ++#define GPIO_INT_ENABLE 0x00 ++#define GPIO_INT_SENSE0 0x04 ++#define GPIO_INT_SENSE1 0x18 ++#define GPIO_INT_SENSE2 0x1c ++#define GPIO_INT_STATUS 0x20 ++#define GPIO_RESET_TOL 0x24 ++/* relative to u32debounce base */ ++#define GPIO_DEBOUNCE_SEL_0 0 ++#define GPIO_DEBOUNCE_SEL_1 4 ++/* relative to u32cmdsrc base */ ++#define GPIO_CMD_SRC_0 0 ++#define GPIO_CMD_SRC_1 4 ++ ++#define PORT_PIN(PORT, PIN) (((PORT) << 3) | ((PIN)&0x07)) ++#define GPIO_PIN(N) (N & 0x07) ++#define GPIO_PORT(N) (N >> 3) ++#define GPIO_SHIFT(PORT, PIN) ((PIN) + (((PORT) % 4) * 8)) ++#define GPIO_GROUP(PORT) ((PORT) / 4) ++ ++#define ID_LED_PORT_PIN PORT_PIN(GPIO_PORT_S, GPIO_PIN_6) ++#define GRN_LED_PORT_PIN PORT_PIN(GPIO_PORT_S, GPIO_PIN_4) ++#define AMB_LED_PORT_PIN PORT_PIN(GPIO_PORT_S, GPIO_PIN_5) ++#define FORCE_BMC_UPDATE_PORT_PIN PORT_PIN(GPIO_PORT_D, GPIO_PIN_0) ++#define TPM_EN_PULSE_PORT_PIN PORT_PIN(GPIO_PORT_D, GPIO_PIN_6) ++ ++ ++// GPIO Configuration Register bits ++#define GPCFG_EVENT_TO_SMI (1 << 7) // 1 == enabled ++#define GPCFG_EVENT_TO_IRQ (1 << 6) // 1 == enabled ++#define GPCFG_DEBOUNCE_EN (1 << 5) // 1 == input debounce, 0 == pulse output ++#define GPCFG_ACTIVE_HIGH (1 << 4) // 1 == Active high ++#define GPCFG_LEVEL_TRIG (1 << 3) // 1 == level (default), 0 == edge ++#define GPCFG_OUTPUT_EN (1 << 0) // 1 == Output enabled ++ ++// GPIO Debounce and Blink Configuration Register bits ++#define GPIO_DEBOUNCE_NONE 0x00 ++#define GPIO_DEBOUNCE_60US 0x01 ++#define GPIO_DEBOUNCE_8MS 0x02 ++#define GPIO_DEBOUNCE_16MS 0x03 ++ ++typedef struct { ++ uint8_t u8PortPin; ++ uint8_t u8PinCFG; ++ uint8_t u8Value; ++ uint8_t u8Debounce; ++} GPIOValue; ++ ++void gpio_init(const GPIOValue* table, size_t count); ++void gpio_set_value(int n, int asserted); ++int gpio_get_value(int n); ++ ++#endif /* __HW_GPIO_H__ */ +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index fca4d91115..252a05dd73 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -14,6 +14,47 @@ + #include <asm/arch/aspeed.h> + + #include "ast-g5.h" ++#include "ast-g5-gpio.h" ++ ++/* Names to match the GPIOs */ ++enum gpio_names { ++ GPIO_ID_LED = 0, ++ GPIO_GREEN_LED, ++ GPIO_AMBER_LED, ++ GPIO_FF_UPD_JUMPER, ++ GPIO_ENABLE_TPM_PULSE, ++}; ++ ++#define GPIO_CFG_DEFAULT (GPCFG_ACTIVE_HIGH | GPCFG_LEVEL_TRIG) ++// Active High, Level, Output Disabled ++ ++#define GPIO_CFG_FP_LED (GPCFG_OUTPUT_EN) ++// Active High, Pull-up, Level, Output Disabled ++ ++// Format is: ++// GPIO PORT, GPIO PIN Number, GPIO PIN Configuration, GPIO PIN Value, GPIO ++// Debounce/Blink Setting ++static const GPIOValue gpio_table[] = { ++ /* ID LED pin S6 - low asserted, 0=on */ ++ [GPIO_ID_LED] = {ID_LED_PORT_PIN, GPIO_CFG_FP_LED, 0, ++ GPIO_DEBOUNCE_NONE}, ++ ++ /* Green LED pin S4 - high asserted, 0=off */ ++ [GPIO_GREEN_LED] = {GRN_LED_PORT_PIN, GPIO_CFG_FP_LED, 1, ++ GPIO_DEBOUNCE_NONE}, ++ ++ /* Amber LED pin S5 - high asserted, 0=off */ ++ [GPIO_AMBER_LED] = {AMB_LED_PORT_PIN, GPIO_CFG_FP_LED, 1, ++ GPIO_DEBOUNCE_NONE}, ++ ++ /* Force Update Jumper -- pin D0 */ ++ [GPIO_FF_UPD_JUMPER] = {FORCE_BMC_UPDATE_PORT_PIN, GPIO_CFG_DEFAULT, 0, ++ GPIO_DEBOUNCE_8MS}, ++ ++ /* Enable Pulse -- pin D6 */ ++ [GPIO_ENABLE_TPM_PULSE] = {PORT_PIN(GPIO_PORT_D, GPIO_PIN_6), ++ GPIO_CFG_DEFAULT, 0, GPIO_DEBOUNCE_8MS}, ++}; + + #define LPC_SNOOP_ADDR 0x80 + #define HICR5 0x080 /* Host Interface Control Register 5 */ +@@ -107,6 +148,7 @@ static void sgpio_init(void) + extern void espi_init(void); + void ast_g5_intel(void) + { ++ gpio_init(gpio_table, ARRAY_SIZE(gpio_table)); + espi_init(); + sgpio_init(); + } +diff --git a/board/aspeed/ast-g5/ast-g5.h b/board/aspeed/ast-g5/ast-g5.h +index 9fd10eccb3..908db1477b 100644 +--- a/board/aspeed/ast-g5/ast-g5.h ++++ b/board/aspeed/ast-g5/ast-g5.h +@@ -3,5 +3,6 @@ + + #include <common.h> + #include "ast-g5-irq.h" ++#include "ast-g5-gpio.h" + + #endif /* _AST_G5_H_ */ +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0010-Update-Force-Firmware-Update-Jumper-to-use-new-gpio.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0010-Update-Force-Firmware-Update-Jumper-to-use-new-gpio.patch new file mode 100644 index 000000000..c97e6d74f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0010-Update-Force-Firmware-Update-Jumper-to-use-new-gpio.patch @@ -0,0 +1,98 @@ +From 1f710737f2fe8dea4bc5ebef1e6011de294764b4 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Fri, 16 Nov 2018 14:59:04 -0800 +Subject: [PATCH] Update Force Firmware Update Jumper to use new gpio API + +Add a function that allows easy reading of the FFUJ from other +functions, such as autoboot. + +Change-Id: I8ead931e9dd828522095a0ef386875be652ec885 +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + .../include/asm/arch-aspeed/ast-g5-intel.h | 19 +++++++++++++++++++ + arch/arm/include/asm/arch-aspeed/platform.h | 1 + + board/aspeed/ast-g5/ast-g5-intel.c | 5 +++++ + common/autoboot.c | 6 ++++++ + 4 files changed, 31 insertions(+) + create mode 100644 arch/arm/include/asm/arch-aspeed/ast-g5-intel.h + +diff --git a/arch/arm/include/asm/arch-aspeed/ast-g5-intel.h b/arch/arm/include/asm/arch-aspeed/ast-g5-intel.h +new file mode 100644 +index 0000000000..cd9a0994fa +--- /dev/null ++++ b/arch/arm/include/asm/arch-aspeed/ast-g5-intel.h +@@ -0,0 +1,19 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifndef __AST_INTEL_G5_H__ ++#define __AST_INTEL_G5_H__ ++ ++#define AST_G5_INTEL 1 ++ ++#ifndef __ASSEMBLY__ ++int intel_force_firmware_jumper_enabled(void); ++#endif ++ ++#endif /* __AST_INTEL_G5_H__ */ +diff --git a/arch/arm/include/asm/arch-aspeed/platform.h b/arch/arm/include/asm/arch-aspeed/platform.h +index 9f339e913a..3ea1c99089 100644 +--- a/arch/arm/include/asm/arch-aspeed/platform.h ++++ b/arch/arm/include/asm/arch-aspeed/platform.h +@@ -27,6 +27,7 @@ + #include <asm/arch/ast2400_platform.h> + #elif defined(AST_SOC_G5) + #include <asm/arch/ast_g5_platform.h> ++#include <asm/arch/ast-g5-intel.h> + #define CONFIG_BOARD_EARLY_INIT_F 1 /* Call board_early_init_f */ + #else + #err "No define for platform.h" +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index 252a05dd73..58ad6a55b8 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -145,6 +145,11 @@ static void sgpio_init(void) + writel(value, AST_GPIO_BASE + GPIO254); + } + ++int intel_force_firmware_jumper_enabled(void) ++{ ++ return gpio_get_value(GPIO_FF_UPD_JUMPER); ++} ++ + extern void espi_init(void); + void ast_g5_intel(void) + { +diff --git a/common/autoboot.c b/common/autoboot.c +index c52bad84a4..d66c0fa63a 100644 +--- a/common/autoboot.c ++++ b/common/autoboot.c +@@ -14,6 +14,7 @@ + #include <menu.h> + #include <post.h> + #include <u-boot/sha256.h> ++#include <asm/arch/platform.h> + + DECLARE_GLOBAL_DATA_PTR; + +@@ -259,6 +260,11 @@ static int abortboot(int bootdelay) + { + int abort = 0; + ++# ifdef AST_G5_INTEL ++ if (intel_force_firmware_jumper_enabled()) ++ return 1; ++# endif ++ + if (bootdelay >= 0) + abort = __abortboot(bootdelay); + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0011-Add-basic-timer-support-for-Aspeed-g5-in-U-Boot.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0011-Add-basic-timer-support-for-Aspeed-g5-in-U-Boot.patch new file mode 100644 index 000000000..11a474b96 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0011-Add-basic-timer-support-for-Aspeed-g5-in-U-Boot.patch @@ -0,0 +1,160 @@ +From 83d67b5b3cbffcefda5efdc0060b9e30f44c9aca Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Fri, 16 Nov 2018 14:44:49 -0800 +Subject: [PATCH 6/7] Add basic timer support for Aspeed g5 in U-Boot + +Timers will be used for timing events and making blinky LEDs. This just +adds the API and infrastructure. + +Change-Id: I8ff03b26070b43a47fb970ddf6124d6c3f29b058 +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + board/aspeed/ast-g5/Makefile | 1 + + board/aspeed/ast-g5/ast-g5-intel.c | 1 + + board/aspeed/ast-g5/ast-g5-timer.c | 66 ++++++++++++++++++++++++++++++ + board/aspeed/ast-g5/ast-g5-timer.h | 27 ++++++++++++ + board/aspeed/ast-g5/ast-g5.h | 1 + + 5 files changed, 96 insertions(+) + create mode 100644 board/aspeed/ast-g5/ast-g5-timer.c + create mode 100644 board/aspeed/ast-g5/ast-g5-timer.h + +diff --git a/board/aspeed/ast-g5/Makefile b/board/aspeed/ast-g5/Makefile +index 2970ae5741..90224333c4 100644 +--- a/board/aspeed/ast-g5/Makefile ++++ b/board/aspeed/ast-g5/Makefile +@@ -3,3 +3,4 @@ obj-y += ast-g5-intel.o + obj-y += ast-g5-espi.o + obj-y += ast-g5-irq.o + obj-y += ast-g5-gpio.o ++obj-y += ast-g5-timer.o +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index 58ad6a55b8..23bf4e4352 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -15,6 +15,7 @@ + + #include "ast-g5.h" + #include "ast-g5-gpio.h" ++#include "ast-g5-timer.h" + + /* Names to match the GPIOs */ + enum gpio_names { +diff --git a/board/aspeed/ast-g5/ast-g5-timer.c b/board/aspeed/ast-g5/ast-g5-timer.c +new file mode 100644 +index 0000000000..56157222d9 +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-timer.c +@@ -0,0 +1,66 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include <common.h> ++#include <asm/io.h> ++#include <asm/arch/regs-scu.h> ++#include <asm/arch/ast_scu.h> ++#include <asm/arch/aspeed.h> ++ ++#include "ast-g5.h" ++#include "ast-g5-timer.h" ++#include "ast-g5-irq.h" ++ ++static const int timer_irqs[] = {16, 17, 18, 35, 37, 37, 38, 39}; ++/* offsets from AST_TIMER_BASE for each timer */ ++static const uint32_t timer_bases[] = {0, 0x10, 0x20, 0x40, ++ 0x50, 0x60, 0x70, 0x80}; ++#define TIMER_1MHZ_CLK_COUNT 1000000u ++#define TIMER_ENABLE 1 ++#define TIMER_1MHZ_CLK_SEL 2 ++#define TIMER_ENABLE_IRQ 4 ++#define TIMER_ENABLE_PULSE 8 ++#define TIMER_CONTROL 0x30 ++#define TIMER_RELOAD 0x04 ++ ++void timer_enable(int n, uint32_t freq, interrupt_handler_t *handler) ++{ ++ if (n < 0 || n > 7) { ++ return; ++ } ++ uint32_t tctrl = readl(AST_TIMER_BASE + TIMER_CONTROL); ++ writel(tctrl & ~(0x0f << (n * 4)), AST_TIMER_BASE + TIMER_CONTROL); ++ ++ // figure out best base for requested frequency ++ // (this will give 1MHz clock preference if period is within 1ms of ++ // requested) ++ uint32_t v = TIMER_1MHZ_CLK_COUNT / freq; ++ if (v > 1000 || v * freq == TIMER_1MHZ_CLK_COUNT) { ++ tctrl |= (TIMER_1MHZ_CLK_SEL << (n * 4)); ++ } else { ++ uint32_t pclk = ast_get_ahbclk(); ++ v = pclk / freq; ++ } ++ writel(v, AST_TIMER_BASE + timer_bases[n] + TIMER_RELOAD); ++ if (handler) { ++ request_irq(timer_irqs[n], handler); ++ tctrl |= (TIMER_ENABLE_IRQ << (n * 4)); ++ } ++ tctrl |= (TIMER_ENABLE << (n * 4)); ++ writel(tctrl, AST_TIMER_BASE + TIMER_CONTROL); ++} ++ ++void timer_disable(int n) ++{ ++ if (n < 0 || n > 7) { ++ return; ++ } ++ uint32_t tctrl = readl(AST_TIMER_BASE + TIMER_CONTROL); ++ writel(tctrl & ~(0x0f << (n * 4)), AST_TIMER_BASE + TIMER_CONTROL); ++} +diff --git a/board/aspeed/ast-g5/ast-g5-timer.h b/board/aspeed/ast-g5/ast-g5-timer.h +new file mode 100644 +index 0000000000..4b1ac28a9f +--- /dev/null ++++ b/board/aspeed/ast-g5/ast-g5-timer.h +@@ -0,0 +1,27 @@ ++/* ++ * Copyright 2018 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifndef __AST_G5_TIMER_H__ ++#define __AST_G5_TIMER_H__ ++ ++#include <common.h> ++ ++#define TIMER_1 0 ++#define TIMER_2 1 ++#define TIMER_3 2 ++#define TIMER_4 3 ++#define TIMER_5 4 ++#define TIMER_6 5 ++#define TIMER_7 6 ++#define TIMER_8 7 ++ ++void timer_enable(int n, uint32_t freq, interrupt_handler_t handler); ++void timer_disable(int n); ++ ++#endif /* __AST_G5_TIMER_H__ */ +diff --git a/board/aspeed/ast-g5/ast-g5.h b/board/aspeed/ast-g5/ast-g5.h +index 908db1477b..28fe5eafcb 100644 +--- a/board/aspeed/ast-g5/ast-g5.h ++++ b/board/aspeed/ast-g5/ast-g5.h +@@ -4,5 +4,6 @@ + #include <common.h> + #include "ast-g5-irq.h" + #include "ast-g5-gpio.h" ++#include "ast-g5-timer.h" + + #endif /* _AST_G5_H_ */ +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0012-Add-status-and-ID-LED-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0012-Add-status-and-ID-LED-support.patch new file mode 100644 index 000000000..5a2c2206f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0012-Add-status-and-ID-LED-support.patch @@ -0,0 +1,144 @@ +From f0e3631ea3005640f988727f051106d83b5dfdaf Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Wed, 14 Nov 2018 12:16:53 -0800 +Subject: [PATCH 7/7] Add status and ID LED support + +Add status (amber and green) and ID (blue) LED support. In the +bootloader phase, the LEDs should be blinking. When booting linux, they +should turn to a fixed state. + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +Change-Id: Ic9595621b21000ef465ff57ed2047855296e2714 +--- + board/aspeed/ast-g5/ast-g5-intel.c | 118 +++++++++++++++++++++++++++++ + 1 file changed, 118 insertions(+) + +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index 23bf4e4352..5ff2cbd0e2 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -146,6 +146,110 @@ static void sgpio_init(void) + writel(value, AST_GPIO_BASE + GPIO254); + } + ++/* running the timer at 48 hertz will easily give a 24Hz blink */ ++#define TICK_HZ 48 ++#define BLINK_DELAY(HZ) ((int)((TICK_HZ / (HZ)) / 2) - 1) ++typedef enum { ++ /* ++ * Identifies the control request for the ID LED. ++ */ ++ EIDLED_Initialize = 0, ++ EIDLED_Tick, ++ EIDLED_Toggle, ++ EIDLED_Off, ++ EIDLED_On, ++ EIDLED_Blink, ++ EIDLED_Blink_24HZ = EIDLED_Blink + BLINK_DELAY(24), ++ EIDLED_Blink_12HZ = EIDLED_Blink + BLINK_DELAY(12), ++ EIDLED_Blink_6HZ = EIDLED_Blink + BLINK_DELAY(6), ++ EIDLED_Blink_3HZ = EIDLED_Blink + BLINK_DELAY(3), ++ EIDLED_Blink_1HZ = EIDLED_Blink + BLINK_DELAY(1), ++ EIDLED_Blink_0_5HZ = EIDLED_Blink + BLINK_DELAY(0.5), ++} EIDLEDControl; ++ ++struct led_info { ++ int gpio; ++ EIDLEDControl mode; ++ int count; ++ int state; ++}; ++ ++static struct led_info s_led_info[] = { ++ [GPIO_ID_LED] = {GPIO_ID_LED, EIDLED_Blink_3HZ, 1, 0}, ++ [GPIO_GREEN_LED] = {GPIO_GREEN_LED, EIDLED_Off, 0, 0}, ++ [GPIO_AMBER_LED] = {GPIO_AMBER_LED, EIDLED_Off, 0, 0}, ++}; ++ ++extern void gpio_set_value(int n, int asserted); ++void id_led_control(int id, int action) ++{ ++ if (id >= ARRAY_SIZE(s_led_info)) { ++ return; ++ } ++ /* don't bother with LEDs that are not initialized */ ++ if (EIDLED_Initialize == s_led_info[id].mode) { ++ return; ++ } ++ ++ /* check for a blinker action */ ++ if (EIDLED_Tick == action) { ++ if (s_led_info[id].mode < EIDLED_Blink) { ++ return; ++ } ++ /* check countdown for blink */ ++ if (s_led_info[id].count == 0) { ++ s_led_info[id].count = ++ s_led_info[id].mode - EIDLED_Blink; ++ s_led_info[id].state = !s_led_info[id].state; ++ } else { ++ s_led_info[id].count--; ++ return; ++ } ++ } else if (EIDLED_Toggle == action) { ++ s_led_info[id].state = !s_led_info[id].state; ++ s_led_info[id].mode = ++ s_led_info[id].state ? EIDLED_On : EIDLED_Off; ++ } else if (action > EIDLED_Toggle) { ++ s_led_info[id].mode = action; ++ if (EIDLED_Off == action) { ++ s_led_info[id].state = 0; ++ } else if (EIDLED_On == action) { ++ s_led_info[id].state = 1; ++ } else if (action >= EIDLED_Blink) { ++ s_led_info[id].count = action - EIDLED_Blink; ++ /* wait for the next tick */ ++ return; ++ } ++ } else if (EIDLED_Initialize == action) { ++ if (s_led_info[id].mode >= EIDLED_Blink) { ++ s_led_info[id].count = ++ s_led_info[id].mode - EIDLED_Blink; ++ } ++ } ++ gpio_set_value(s_led_info[id].gpio, s_led_info[id].state); ++} ++ ++static void timer8_irq_handler(void *regs) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(s_led_info); i++) { ++ id_led_control(i, EIDLED_Tick); ++ } ++} ++ ++void timer8_init(void) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(s_led_info); i++) { ++ id_led_control(i, EIDLED_Initialize); ++ } ++ ++ /* set up the timer to fire at TICK_HZ HZ */ ++ timer_enable(TIMER_8, TICK_HZ, timer8_irq_handler); ++} ++ + int intel_force_firmware_jumper_enabled(void) + { + return gpio_get_value(GPIO_FF_UPD_JUMPER); +@@ -157,4 +269,10 @@ void ast_g5_intel(void) + gpio_init(gpio_table, ARRAY_SIZE(gpio_table)); + espi_init(); + sgpio_init(); ++ timer8_init(); ++ if (intel_force_firmware_jumper_enabled()) { ++ id_led_control(GPIO_AMBER_LED, EIDLED_On); ++ } else { ++ id_led_control(GPIO_GREEN_LED, EIDLED_On); ++ } + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0013-aspeed-Add-Pwm-Driver.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0013-aspeed-Add-Pwm-Driver.patch new file mode 100644 index 000000000..d235aea62 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0013-aspeed-Add-Pwm-Driver.patch @@ -0,0 +1,132 @@ +From 3134584998f624bb6c4ee11102b0bd9b7bb1cbba Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Fri, 16 Nov 2018 15:57:57 -0800 +Subject: [PATCH 1/1] aspeed: add Pwm Driver + +Change-Id: Ia8b80212f7c70aafcc6a71782936ec95cf9b7f38 +--- + board/aspeed/ast-g5/ast-g5-intel.c | 105 +++++++++++++++++++++++++++++ + 1 file changed, 105 insertions(+) + +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index 5ff2cbd0e2..f810ded4e7 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -263,9 +263,114 @@ int intel_force_firmware_jumper_enabled(void) + return gpio_get_value(GPIO_FF_UPD_JUMPER); + } + ++/* PWM offsets */ ++ ++#define PWM_BASE_ADDR 0x1E786000 ++#define PWM_CONTROL 0x00 ++#define PWM_CLOCK_SELECTION 0x04 ++#define PWM_DUTY_CYCLE 0x08 ++#define PWM_M0 0x10 ++#define PWM_M1 0x14 ++#define PWM_N0 0x18 ++#define PWM_N1 0x1c ++#define PWM_CONTROL_EXT 0x40 ++#define PWM_CLOCK_SEL_EXT 0x44 ++#define PWM_O0 0x50 ++#define PWM_O1 0x54 ++#define PWM_CHANNEL_COUNT 8 ++ ++#define PWM_CLK_ENABLE BIT(0) ++#define PWM_DUTY(PCT) (((PCT) * 128) / 100) ++#define PWM_DUTY_VALUE PWM_DUTY(57) ++ ++ ++static inline uint32_t ast_scu_read(uint32_t reg) ++{ ++ uint32_t val = readl(AST_SCU_BASE + reg); ++ ++ debug("ast_scu_read : reg = 0x%08x, val = 0x%08x\n", reg, val); ++ return val; ++} ++ ++static inline void ast_scu_write(uint32_t val, uint32_t reg) ++{ ++ debug("ast_scu_write : reg = 0x%08x, val = 0x%08x\n", reg, val); ++ ++ writel(SCU_PROTECT_UNLOCK, AST_SCU_BASE); ++ writel(val, AST_SCU_BASE + reg); ++#ifdef CONFIG_AST_SCU_LOCK ++ writel(0xaa, AST_SCU_BASE); ++#endif ++} ++ ++static void pwm_init(void) ++{ ++ uint32_t val; ++ uint32_t chan; ++ ++ /* select pwm 0-7 */ ++ val = ast_scu_read(AST_SCU_FUN_PIN_CTRL3); ++ val |= (SCU_FUN_PIN_VPIG7 | SCU_FUN_PIN_VPIG6 | SCU_FUN_PIN_VPIG5 ++ | SCU_FUN_PIN_VPIG4 | SCU_FUN_PIN_VPIG3 | SCU_FUN_PIN_VPIG2 ++ | SCU_FUN_PIN_VPIG1 | SCU_FUN_PIN_VPIG0); ++ ast_scu_write(val, AST_SCU_FUN_PIN_CTRL3); ++ ++ /* disable video output mux */ ++ val = ast_scu_read(AST_SCU_FUN_PIN_CTRL5); ++ val &= 0xffffffcf; ++ ast_scu_write(val, AST_SCU_FUN_PIN_CTRL5); ++ val = readl(AST_SCU_FUN_PIN_CTRL6); ++ val &= 0xfffffffc; ++ ast_scu_write(val, AST_SCU_FUN_PIN_CTRL6); ++ ++ /* SCU reset of PWM module */ ++ val = ast_scu_read(AST_SCU_RESET); ++ val |= SCU_RESET_PWM; ++ ast_scu_write(val, AST_SCU_RESET); ++ val &= ~SCU_RESET_PWM; ++ ast_scu_write(val, AST_SCU_RESET); ++ ++ /* set M, N, and 0 clock regs to 0 */ ++ writel(0, PWM_BASE_ADDR + PWM_M0); ++ writel(0, PWM_BASE_ADDR + PWM_N0); ++ writel(0, PWM_BASE_ADDR + PWM_O0); ++ ++ /* disable fans and tachos, set M type control */ ++ writel(0x1, PWM_BASE_ADDR + PWM_CONTROL); ++ writel(0x1, PWM_BASE_ADDR + PWM_CONTROL_EXT); ++ ++ /* enable pwm channels */ ++ for (chan = 0; chan < PWM_CHANNEL_COUNT; chan++) { ++ uint32_t base = chan < 4 ? PWM_BASE_ADDR : PWM_BASE_ADDR + 0x40; ++ uint8_t ch_duty_shift = 16 * (chan & 0x1); ++ uint8_t ch_pair = (chan & 0x3) / 2; ++ ++ /* enable pwm for the channel */ ++ val = readl(base); ++ val |= ((1 << (chan & 0x3)) << 8); ++ writel(val, base); ++ ++ /* set duty cycle */ ++ val = readl(base + PWM_DUTY_CYCLE + ch_pair * 4); ++ val &= ~(0xffff << ch_duty_shift); ++ val |= (((uint32_t)PWM_DUTY_VALUE) << 8) << ch_duty_shift; ++ writel(val, base + PWM_DUTY_CYCLE + ch_pair * 4); ++ } ++ ++ /* set up clock type M: period = 127 units at 24MHz/8 (resulting ~23kHz period) */ ++ writel(0x7f30, PWM_BASE_ADDR + PWM_CLOCK_SELECTION); ++ ++ /* enable pwm-tacho */ ++ ++ val = readl(PWM_BASE_ADDR + PWM_CONTROL); ++ val |= PWM_CLK_ENABLE; ++ writel(val, PWM_BASE_ADDR + PWM_CONTROL); ++} ++ + extern void espi_init(void); + void ast_g5_intel(void) + { ++ pwm_init(); + gpio_init(gpio_table, ARRAY_SIZE(gpio_table)); + espi_init(); + sgpio_init(); +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0014-Keep-interrupts-enabled-until-last-second.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0014-Keep-interrupts-enabled-until-last-second.patch new file mode 100644 index 000000000..91665e064 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0014-Keep-interrupts-enabled-until-last-second.patch @@ -0,0 +1,91 @@ +From 2078771e0ff84be710250b2e9b2e887f7238f9cc Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Sat, 17 Nov 2018 14:17:27 -0800 +Subject: [PATCH 2/3] Keep interrupts enabled until last second + +The U-Boot bootm command disabled interrupts almost first thing. This +would prevent a person hitting the power button on the host immediatly +after AC on because the BMC would fail to respond to the espi interrupts +and the host would power off. + +Change-Id: I6c0fb5cca1be6c326da4c9a3d3dfbab89dac9928 +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + board/aspeed/ast-g5/ast-g5-intel.c | 8 ++++++++ + common/bootm.c | 7 ------- + common/bootm_os.c | 1 + + 3 files changed, 9 insertions(+), 7 deletions(-) + +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index f810ded4e7..4d399be392 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -263,6 +263,14 @@ int intel_force_firmware_jumper_enabled(void) + return gpio_get_value(GPIO_FF_UPD_JUMPER); + } + ++void arch_preboot_os(void) ++{ ++ // last second before booting... set the LEDs ++ id_led_control(GPIO_ID_LED, EIDLED_On); ++ id_led_control(GPIO_GREEN_LED, EIDLED_On); ++ id_led_control(GPIO_AMBER_LED, EIDLED_Off); ++} ++ + /* PWM offsets */ + + #define PWM_BASE_ADDR 0x1E786000 +diff --git a/common/bootm.c b/common/bootm.c +index 2431019b3f..46909ecdbb 100644 +--- a/common/bootm.c ++++ b/common/bootm.c +@@ -602,7 +602,6 @@ int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + int states, bootm_headers_t *images, int boot_progress) + { + boot_os_fn *boot_fn; +- ulong iflag = 0; + int ret = 0, need_boot_fn; + + images->state |= states; +@@ -626,7 +625,6 @@ int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + if (!ret && (states & BOOTM_STATE_LOADOS)) { + ulong load_end; + +- iflag = bootm_disable_interrupts(); + ret = bootm_load_os(images, &load_end, 0); + if (ret == 0) + lmb_reserve(&images->lmb, images->os.load, +@@ -670,8 +668,6 @@ int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP | + BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO); + if (boot_fn == NULL && need_boot_fn) { +- if (iflag) +- enable_interrupts(); + printf("ERROR: booting os '%s' (%d) is not supported\n", + genimg_get_os_name(images->os.os), images->os.os); + bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS); +@@ -711,9 +707,6 @@ int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + + /* Deal with any fallout */ + err: +- if (iflag) +- enable_interrupts(); +- + if (ret == BOOTM_ERR_UNIMPLEMENTED) + bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL); + else if (ret == BOOTM_ERR_RESET) +diff --git a/common/bootm_os.c b/common/bootm_os.c +index 9ec84bd0db..b56eb39780 100644 +--- a/common/bootm_os.c ++++ b/common/bootm_os.c +@@ -476,6 +476,7 @@ __weak void arch_preboot_os(void) + int boot_selected_os(int argc, char * const argv[], int state, + bootm_headers_t *images, boot_os_fn *boot_fn) + { ++ disable_interrupts(); + arch_preboot_os(); + boot_fn(state, argc, argv, images); + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0015-Rewrite-memmove-to-optimize-on-word-transfers.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0015-Rewrite-memmove-to-optimize-on-word-transfers.patch new file mode 100644 index 000000000..475b8c3ff --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0015-Rewrite-memmove-to-optimize-on-word-transfers.patch @@ -0,0 +1,114 @@ +From a71794fc928429e199c5ea48181e5edfbb0c4f39 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Mon, 19 Nov 2018 11:04:02 -0800 +Subject: [PATCH] Rewrite memmove to optimize on word transfers + +Reading from the flash at boot time was using byte-sized transfers, +which ultimately turns into four word transfers over spi for every real +word read. This change breaks memmove down into a header, body, and +trailer, where the body is all done with word-sized transfers. + +Change-Id: Ie0a1f3261e507fb34a908571883d9bf04a1059ee +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + lib/string.c | 77 +++++++++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 64 insertions(+), 13 deletions(-) + +diff --git a/lib/string.c b/lib/string.c +index 67d5f6a421..0bf472f1f6 100644 +--- a/lib/string.c ++++ b/lib/string.c +@@ -505,26 +505,77 @@ void * memcpy(void *dest, const void *src, size_t count) + * + * Unlike memcpy(), memmove() copes with overlapping areas. + */ +-void * memmove(void * dest,const void *src,size_t count) ++void *memmove(void *dest, const void *src, size_t count) + { +- char *tmp, *s; +- +- if (src == dest) ++ unsigned char *bdst = (unsigned char *)dest; ++ const unsigned char *bsrc = (const unsigned char *)src; ++ unsigned long *ldst; ++ const unsigned long *lsrc; ++ size_t unaligned_header = 0, unaligned_trailer = 0; ++ size_t unaligned_src = ++ (sizeof(*ldst) - (size_t)src) & (sizeof(*ldst) - 1); ++ size_t unaligned_dst = ++ (sizeof(*ldst) - (size_t)dest) & (sizeof(*ldst) - 1); ++ ++ if (src == dest || !count) + return dest; + ++ if (unaligned_src || unaligned_dst) { ++ if (unaligned_dst != unaligned_src) { ++ unaligned_header = count; ++ } else { ++ unaligned_header = unaligned_src; ++ if (unaligned_header > count) { ++ unaligned_header = count; ++ } ++ } ++ count -= unaligned_header; ++ } ++ if (count & (sizeof(*ldst) - 1)) { ++ unaligned_trailer = count & (sizeof(*ldst) - 1); ++ count -= unaligned_trailer; ++ } ++ + if (dest <= src) { +- tmp = (char *) dest; +- s = (char *) src; +- while (count--) +- *tmp++ = *s++; ++ /* possible un-aligned bytes */ ++ while (unaligned_header--) ++ *bdst++ = *bsrc++; ++ ++ /* aligned words */ ++ ldst = (unsigned long *)bdst; ++ lsrc = (const unsigned long *)bsrc; ++ while (count >= sizeof(*ldst)) { ++ count -= sizeof(*ldst); ++ *ldst++ = *lsrc++; + } +- else { +- tmp = (char *) dest + count; +- s = (char *) src + count; +- while (count--) +- *--tmp = *--s; ++ ++ /* possibly un-aligned bytes */ ++ bdst = (unsigned char *)ldst; ++ bsrc = (const unsigned char *)lsrc; ++ while (unaligned_trailer--) ++ *bdst++ = *bsrc++; ++ } else { ++ bdst += unaligned_header + count + unaligned_trailer; ++ bsrc += unaligned_header + count + unaligned_trailer; ++ ++ /* possibly un-aligned bytes */ ++ while (unaligned_trailer--) ++ *--bdst = *--bsrc; ++ ++ /* aligned words */ ++ ldst = (unsigned long *)bdst; ++ lsrc = (unsigned long *)bsrc; ++ while (count >= sizeof(*ldst)) { ++ count -= sizeof(*ldst); ++ *--ldst = *--lsrc; + } + ++ /* possibly un-aligned bytes */ ++ bdst = (unsigned char *)ldst; ++ bsrc = (const unsigned char *)lsrc; ++ while (unaligned_header--) ++ *--bdst = *--bsrc; ++ } + return dest; + } + #endif +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0016-Add-support-for-128MB-Macronix-spi-flash-MX66L1G45G.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0016-Add-support-for-128MB-Macronix-spi-flash-MX66L1G45G.patch new file mode 100644 index 000000000..cc1e56fdb --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0016-Add-support-for-128MB-Macronix-spi-flash-MX66L1G45G.patch @@ -0,0 +1,28 @@ +From 8992df8f3a0f5fc16ec41ad6dd7a5a13e6f94d32 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Wed, 5 Dec 2018 16:13:15 -0800 +Subject: [PATCH] Add support for 128MB Macronix spi flash MX66L1G45G + +This will enable u-boot support for the Macronix MX66L1G45G part. + +Change-Id: I5edc69357a8b1607c5c44e53bed3eddf38fdc0be +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + drivers/mtd/spi/sf_params.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/mtd/spi/sf_params.c b/drivers/mtd/spi/sf_params.c +index c577d9ed6c..d2a96efe48 100644 +--- a/drivers/mtd/spi/sf_params.c ++++ b/drivers/mtd/spi/sf_params.c +@@ -50,6 +50,7 @@ const struct spi_flash_params spi_flash_params_table[] = { + {"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, + {"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP}, + {"MX25L51235F", 0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP}, ++ {"MX66L1G45G", 0xc2201b, 0x0, 64 * 1024, 2048, RD_FULL, SECT_4K|WR_QPP}, + {"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, + #endif + #ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0017-Enable-Macronix-and-Micron-SPI-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0017-Enable-Macronix-and-Micron-SPI-support.patch new file mode 100644 index 000000000..b26803c95 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0017-Enable-Macronix-and-Micron-SPI-support.patch @@ -0,0 +1,54 @@ +From 8415002a2e77a41831b3064dae264f60996ac88a Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Thu, 6 Dec 2018 15:07:09 -0800 +Subject: [PATCH] Enable Macronix and Micron SPI support + +This commit enables Macronix and Micron SPI support. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + configs/ast_g5_ncsi_2boot_defconfig | 2 ++ + configs/ast_g5_ncsi_defconfig | 2 ++ + configs/ast_g5_phy_defconfig | 2 ++ + 3 files changed, 6 insertions(+) + +diff --git a/configs/ast_g5_ncsi_2boot_defconfig b/configs/ast_g5_ncsi_2boot_defconfig +index d5b7894a9e00..abceed817615 100644 +--- a/configs/ast_g5_ncsi_2boot_defconfig ++++ b/configs/ast_g5_ncsi_2boot_defconfig +@@ -10,6 +10,8 @@ CONFIG_FIT_VERBOSE=y + CONFIG_HUSH_PARSER=y + CONFIG_OF_LIBFDT=y + CONFIG_SPI_FLASH=y ++CONFIG_SPI_FLASH_MACRONIX=y ++CONFIG_SPI_FLASH_STMICRO=y + CONFIG_SYS_NS16550=y + CONFIG_FIRMWARE_2ND_BOOT=y + CONFIG_AUTOBOOT=y +diff --git a/configs/ast_g5_ncsi_defconfig b/configs/ast_g5_ncsi_defconfig +index 9481e5fb6e9d..3f504a325649 100644 +--- a/configs/ast_g5_ncsi_defconfig ++++ b/configs/ast_g5_ncsi_defconfig +@@ -10,5 +10,7 @@ CONFIG_FIT_VERBOSE=y + CONFIG_HUSH_PARSER=y + CONFIG_OF_LIBFDT=y + CONFIG_SPI_FLASH=y ++CONFIG_SPI_FLASH_MACRONIX=y ++CONFIG_SPI_FLASH_STMICRO=y + CONFIG_SYS_NS16550=y + CONFIG_USE_IRQ=y +diff --git a/configs/ast_g5_phy_defconfig b/configs/ast_g5_phy_defconfig +index 4aefcf49e880..8f0919043376 100644 +--- a/configs/ast_g5_phy_defconfig ++++ b/configs/ast_g5_phy_defconfig +@@ -11,5 +11,7 @@ CONFIG_FIT_VERBOSE=y + CONFIG_HUSH_PARSER=y + CONFIG_OF_LIBFDT=y + CONFIG_SPI_FLASH=y ++CONFIG_SPI_FLASH_MACRONIX=y ++CONFIG_SPI_FLASH_STMICRO=y + CONFIG_SYS_NS16550=y + CONFIG_USE_IRQ=y +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0018-Add-support-for-Macronix-and-Micron-1Gbits-SPI-flash.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0018-Add-support-for-Macronix-and-Micron-1Gbits-SPI-flash.patch new file mode 100644 index 000000000..b96211118 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0018-Add-support-for-Macronix-and-Micron-1Gbits-SPI-flash.patch @@ -0,0 +1,65 @@ +From 5f2a7c03c877454eb78a406934abf6b7d05e40a6 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Thu, 6 Dec 2018 18:49:04 -0800 +Subject: [PATCH] Add support for Macronix and Micron 1Gbits SPI flash + +Quick fix to support Macronix and Micron 1Gbits SPI. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/mach-aspeed/flash.c | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/arch/arm/mach-aspeed/flash.c b/arch/arm/mach-aspeed/flash.c +index f9f1345523d4..abc0420176a6 100644 +--- a/arch/arm/mach-aspeed/flash.c ++++ b/arch/arm/mach-aspeed/flash.c +@@ -79,6 +79,8 @@ flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* FLASH chips info */ + #define MX25L12805D 0x1820C2 + #define MX25L25635E 0x1920C2 + #define MX66L51235F 0x1A20C2 ++#define MX66L1G45G 0x1B20C2 ++#define MT25QL01GB 0x21BA20 + #define SST25VF016B 0x4125bf + #define SST25VF064C 0x4b25bf + #define SST25VF040B 0x8d25bf +@@ -977,6 +979,36 @@ static ulong flash_get_size (ulong base, flash_info_t *info) + #endif + break; + ++ case MT25QL01GB: ++ case MX66L1G45G: ++ erase_region_size = 0x10000; ++ info->readcmd = 0x0b; ++ info->dualport = 0; ++ info->dummybyte = 1; ++ info->buffersize = 256; ++ WriteClk = 50; ++ EraseClk = 20; ++ ReadClk = 50; ++#if 1 ++ info->sector_count = 2048; ++ info->size = 0x4000000; ++ info->address32 = 1; ++#if defined(CONFIG_FLASH_SPIx2_Dummy) ++ info->readcmd = 0xbb; ++ info->dummybyte = 1; ++ info->dualport = 1; ++ info->iomode = IOMODEx2_dummy; ++#elif defined(CONFIG_FLASH_SPIx4_Dummy) ++ info->readcmd = 0xeb; ++ info->dummybyte = 3; ++ info->dualport = 0; ++ info->iomode = IOMODEx4_dummy; ++ info->quadport = 1; ++ info->dummydata = 0xaa; ++#endif ++#endif ++ break; ++ + case MX25L12805D: + info->sector_count = 256; + info->size = 0x1000000; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0019-u-boot-full-platform-reset-espi-oob-ready.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0019-u-boot-full-platform-reset-espi-oob-ready.patch new file mode 100644 index 000000000..1d2b02954 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0019-u-boot-full-platform-reset-espi-oob-ready.patch @@ -0,0 +1,48 @@ +From bb490aa226dcf261d3d6865be37130765ecbe9f4 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Mon, 17 Dec 2018 20:37:23 -0800 +Subject: [PATCH] u-boot: full platform reset + espi oob-ready + +If the platform is strapped for fast reset, have platform-g5.S do a full +reset and then immediately set oob-ready so the espi master controller +can initiate communication. + +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> +--- + arch/arm/mach-aspeed/platform_g5.S | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/mach-aspeed/platform_g5.S b/arch/arm/mach-aspeed/platform_g5.S +index 2ac1ca4721..66427b6f33 100644 +--- a/arch/arm/mach-aspeed/platform_g5.S ++++ b/arch/arm/mach-aspeed/platform_g5.S +@@ -139,7 +139,7 @@ + But if FW has other initial code executed before platform.S, then it should use WDT_SOC mode. + Use WDT_Full may clear the initial result of prior initial code. + ******************************************************************************/ +-//#define ASTMMC_INIT_RESET_MODE_FULL ++#define ASTMMC_INIT_RESET_MODE_FULL + + /****************************************************************************** + There is a compatibility issue for Hynix DDR4 SDRAM. +@@ -563,6 +563,17 @@ wait_first_reset: + *******************************************/ + + bypass_first_reset: ++ /* Timing from ESPI master requires OOB channel ready bit be set early */ ++ ldr r0, =0x1e6e2070 @ check strapping for eSPI mode ++ tst r0, #0x02000000 @ Test for bit 25 - eSPI Mode ++ beq espi_early_init_done @ if bit 25 clear, dont set OOB ready ++ ++ ldr r0, =0x1e6ee000 ++ ldr r1, [r0] @ ESPI000: ESPI Engine Control Reg ++ orr r1, r1, #0x00000010 @ Set OOB Channel Ready bit ++ str r1, [r0] ++espi_early_init_done: ++ + /* Enable Timer separate clear mode */ + ldr r0, =0x1e782038 + mov r1, #0xAE +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0020-Add-system-reset-status-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0020-Add-system-reset-status-support.patch new file mode 100644 index 000000000..814a7dc2a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0020-Add-system-reset-status-support.patch @@ -0,0 +1,206 @@ +From d0338f68edb7f818b45ee43765cf124c14ccae03 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Tue, 22 Jan 2019 16:15:36 +0800 +Subject: [PATCH] Add system reset status support + +Will display the reset reasons and other CPU information in u-boot, +and save the reset reasons into kernel command line, +for applications to query. + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + arch/arm/include/asm/arch-aspeed/ast_scu.h | 1 + + arch/arm/include/asm/arch-aspeed/platform.h | 2 ++ + arch/arm/mach-aspeed/ast-scu.c | 55 +++++++++++++++++++++++++++++ + board/aspeed/ast-g5/ast-g5-intel.c | 30 ++++++++++++++++ + board/aspeed/ast-g5/ast-g5.c | 7 ++++ + 5 files changed, 95 insertions(+) + +diff --git a/arch/arm/include/asm/arch-aspeed/ast_scu.h b/arch/arm/include/asm/arch-aspeed/ast_scu.h +index c10e6a9..369c4e3 100644 +--- a/arch/arm/include/asm/arch-aspeed/ast_scu.h ++++ b/arch/arm/include/asm/arch-aspeed/ast_scu.h +@@ -37,6 +37,7 @@ extern void ast_scu_get_who_init_dram(void); + + extern u32 ast_get_clk_source(void); + extern u32 ast_get_h_pll_clk(void); ++extern u32 ast_get_m_pll_clk(void); + extern u32 ast_get_ahbclk(void); + + extern u32 ast_scu_get_vga_memsize(void); +diff --git a/arch/arm/include/asm/arch-aspeed/platform.h b/arch/arm/include/asm/arch-aspeed/platform.h +index 3b06e52..4e4140d 100644 +--- a/arch/arm/include/asm/arch-aspeed/platform.h ++++ b/arch/arm/include/asm/arch-aspeed/platform.h +@@ -29,6 +29,8 @@ + #include <asm/arch/ast_g5_platform.h> + #include <asm/arch/ast-g5-intel.h> + #define CONFIG_BOARD_EARLY_INIT_F 1 /* Call board_early_init_f */ ++#define CONFIG_BOARD_LATE_INIT 1 /* Call board_late_init */ ++#define CONFIG_DISPLAY_CPUINFO 1 + #else + #err "No define for platform.h" + #endif +diff --git a/arch/arm/mach-aspeed/ast-scu.c b/arch/arm/mach-aspeed/ast-scu.c +index 63e9c7c..f4268f3 100644 +--- a/arch/arm/mach-aspeed/ast-scu.c ++++ b/arch/arm/mach-aspeed/ast-scu.c +@@ -100,6 +100,7 @@ static struct soc_id soc_map_table[] = { + SOC_ID("AST2510-A1", 0x04010103), + SOC_ID("AST2520-A1", 0x04010203), + SOC_ID("AST2530-A1", 0x04010403), ++ SOC_ID("AST2500-A2", 0x04030303), + }; + + void ast_scu_init_eth(u8 num) +@@ -235,6 +236,29 @@ u32 ast_get_h_pll_clk(void) + return clk; + } + ++u32 ast_get_m_pll_clk(void) ++{ ++ u32 clk=0; ++ u32 m_pll_set = ast_scu_read(AST_SCU_M_PLL); ++ ++ if(m_pll_set & SCU_M_PLL_OFF) ++ return 0; ++ ++ // Programming ++ clk = ast_get_clk_source(); ++ if(m_pll_set & SCU_M_PLL_BYPASS) { ++ return clk; ++ } else { ++ //PD == SCU20[13:18] ++ //M == SCU20[5:12] ++ //N == SCU20[0:4] ++ //mpll = 24MHz * [(M+1) /(N+1)] / (P+1) ++ clk = ((clk * (SCU_M_PLL_GET_MNUM(m_pll_set) + 1)) / (SCU_M_PLL_GET_NNUM(m_pll_set) + 1))/(SCU_M_PLL_GET_PDNUM(m_pll_set) + 1); ++ } ++ debug("m_pll = %d\n",clk); ++ return clk; ++} ++ + u32 ast_get_ahbclk(void) + { + unsigned int axi_div, ahb_div, hpll; +@@ -304,6 +328,33 @@ u32 ast_get_h_pll_clk(void) + return clk; + } + ++u32 ast_get_m_pll_clk(void) ++{ ++ u32 clk=0; ++ u32 m_pll_set = ast_scu_read(AST_SCU_M_PLL); ++ ++ if(m_pll_set & SCU_M_PLL_OFF) ++ return 0; ++ ++ // Programming ++ clk = ast_get_clk_source(); ++ if(m_pll_set & SCU_M_PLL_BYPASS) { ++ return clk; ++ } else { ++ //OD == SCU24[4] ++ //OD = SCU_M_PLL_GET_DIV(h_pll_set); ++ //Numerator == SCU24[10:5] ++ //num = SCU_M_PLL_GET_NUM(h_pll_set); ++ //Denumerator == SCU24[3:0] ++ //denum = SCU_M_PLL_GET_DENUM(h_pll_set); ++ ++ //hpll = 24MHz * (2-OD) * ((Numerator+2)/(Denumerator+1)) ++ clk = (clk * (2-SCU_M_PLL_GET_DIV(m_pll_set)) * ((SCU_M_PLL_GET_NUM(m_pll_set)+2)/(SCU_M_PLL_GET_DENUM(m_pll_set)+1))); ++ } ++ debug("m_pll = %d\n",clk); ++ return clk; ++} ++ + u32 ast_get_ahbclk(void) + { + unsigned int div, hpll; +@@ -452,6 +503,9 @@ void ast_scu_sys_rest_info(void) + { + u32 rest = ast_scu_read(AST_SCU_SYS_CTRL); + ++#ifdef AST_SOC_G5 ++ printf("RST : 0x%02x\n", rest); ++#else + if (rest & SCU_SYS_EXT_RESET_FLAG) { + printf("RST : External\n"); + ast_scu_write(SCU_SYS_EXT_RESET_FLAG, AST_SCU_SYS_CTRL); +@@ -464,6 +518,7 @@ void ast_scu_sys_rest_info(void) + } else { + printf("RST : CLK en\n"); + } ++#endif + } + + u32 ast_scu_get_vga_memsize(void) +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index bcaf81e..1e8708a 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -303,6 +303,36 @@ static inline void ast_scu_write(uint32_t val, uint32_t reg) + #endif + } + ++void ast_g5_intel_late_init(void) ++{ ++ char *cmdline = NULL; ++ char *cmdline_new = NULL; ++ char buf[32]; ++ u32 rest = 0; ++ ++ /* save and clear reset status */ ++ rest = ast_scu_read(AST_SCU_SYS_CTRL); ++ snprintf(buf, sizeof(buf), " resetreason=0x%x", rest); ++ ast_scu_write(0, AST_SCU_SYS_CTRL); ++ ++ cmdline = getenv("bootargs"); ++ if (!cmdline) { ++ printf("Get bootargs fail!\n"); ++ return; ++ } ++ ++ cmdline_new = malloc(strlen(cmdline) + strlen(buf) + 1); ++ if (!cmdline_new) { ++ printf("Cannot malloc memory!\n"); ++ return; ++ } ++ ++ /* append the reset status into kernel command line */ ++ snprintf(cmdline_new, strlen(cmdline) + strlen(buf) + 1, "%s%s", cmdline, buf); ++ setenv("bootargs", cmdline_new); ++ free(cmdline_new); ++} ++ + static void pwm_init(void) + { + uint32_t val; +diff --git a/board/aspeed/ast-g5/ast-g5.c b/board/aspeed/ast-g5/ast-g5.c +index d41ef9c..0953677 100644 +--- a/board/aspeed/ast-g5/ast-g5.c ++++ b/board/aspeed/ast-g5/ast-g5.c +@@ -19,6 +19,7 @@ + DECLARE_GLOBAL_DATA_PTR; + + extern void ast_g5_intel(void); ++extern void ast_g5_intel_late_init(void); + + int board_early_init_f(void) + { +@@ -40,6 +41,12 @@ int board_init(void) + return 0; + } + ++int board_late_init(void) ++{ ++ ast_g5_intel_late_init(); ++ return 0; ++} ++ + int dram_init(void) + { + u32 vga = ast_scu_get_vga_memsize(); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0020-Enable-PCIe-L1-support.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0020-Enable-PCIe-L1-support.patch new file mode 100644 index 000000000..bdf3d2ddd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0020-Enable-PCIe-L1-support.patch @@ -0,0 +1,38 @@ +From 22c61ba094d8ebdbdcb44f848eef1f5d87a4be87 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 8 Jan 2019 13:33:15 -0800 +Subject: [PATCH] Enable PCIe L1 support + +This commit enables PCIe L1 support using magic registers. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/mach-aspeed/platform_g5.S | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/arch/arm/mach-aspeed/platform_g5.S b/arch/arm/mach-aspeed/platform_g5.S +index 66427b6f33e7..b4043534b083 100644 +--- a/arch/arm/mach-aspeed/platform_g5.S ++++ b/arch/arm/mach-aspeed/platform_g5.S +@@ -2432,6 +2432,18 @@ spi_cbr_end: + bic r1, r1, #0x00400000 + str r1, [r0] + ++ ldr r0, =0x1e6ed07c @ Enable PCIe L1 support ++ ldr r1, =0xa8 ++ str r1, [r0] ++ ++ ldr r0, =0x1e6ed068 ++ ldr r1, =0xc81f0a ++ str r1, [r0] ++ ++ ldr r0, =0x1e6ed07c ++ mov r1, #0 ++ str r1, [r0] ++ + /****************************************************************************** + Configure MAC timing + ******************************************************************************/ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0021-Config-host-uart-clock-source-using-environment-vari.patch b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0021-Config-host-uart-clock-source-using-environment-vari.patch new file mode 100755 index 000000000..310d9359b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/files/0021-Config-host-uart-clock-source-using-environment-vari.patch @@ -0,0 +1,108 @@ +From c82ba33ea40e0007945cbc93da58f296fdeedeaf Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 11 Feb 2019 15:19:56 +0800 +Subject: [PATCH] Config host uart clock source using environment variable + +In order to support high speed uart for host uarts, +the uart clock needs to be switched between 24M and 192M. +Config SCU4C based on environment variable hostserialcfg, +this variable is set by IPMI OEM commands. + +Tested: +Change the hostserialcfg variable to 0/1/2/3, +using the below commands: + +ipmitool raw 0x32 0x90 1 0; reboot +cat /sys/class/tty/ttyS*/uartclk, all should be 24MHz + +ipmitool raw 0x32 0x90 1 1; reboot +cat /sys/class/tty/ttyS*/uartclk, ttyS0/2/3 should be 192MHz + +ipmitool raw 0x32 0x90 1 2; reboot +cat /sys/class/tty/ttyS*/uartclk, ttyS1 should be 192MHz + +ipmitool raw 0x32 0x90 1 3; reboot +cat /sys/class/tty/ttyS*/uartclk, ttyS0/12/3 should be 192MHz + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + arch/arm/include/asm/arch-aspeed/regs-scu.h | 5 ++++ + board/aspeed/ast-g5/ast-g5-intel.c | 39 +++++++++++++++++++++++++++++ + 2 files changed, 44 insertions(+) + +diff --git a/arch/arm/include/asm/arch-aspeed/regs-scu.h b/arch/arm/include/asm/arch-aspeed/regs-scu.h +index 10b983a..8a596ce 100644 +--- a/arch/arm/include/asm/arch-aspeed/regs-scu.h ++++ b/arch/arm/include/asm/arch-aspeed/regs-scu.h +@@ -529,6 +529,11 @@ + /* AST_SCU_MAC_CLK 0x48 - MAC interface clock delay setting register */ + + /* AST_SCU_MISC2_CTRL 0x4C - Misc. 2 Control register */ ++#define SCU_UART5_HS_CLOCK (1 << 28) ++#define SCU_UART4_HS_CLOCK (1 << 27) ++#define SCU_UART3_HS_CLOCK (1 << 26) ++#define SCU_UART2_HS_CLOCK (1 << 25) ++#define SCU_UART1_HS_CLOCK (1 << 24) + #ifdef AST_SOC_G5 + #define SCU_PCIE_MAPPING_HIGH (1 << 15) + #define SCU_MALI_DTY_MODE (1 << 8) +diff --git a/board/aspeed/ast-g5/ast-g5-intel.c b/board/aspeed/ast-g5/ast-g5-intel.c +index 1e8708a..f810a40 100644 +--- a/board/aspeed/ast-g5/ast-g5-intel.c ++++ b/board/aspeed/ast-g5/ast-g5-intel.c +@@ -103,6 +103,9 @@ static const GPIOValue gpio_table[] = { + #define SGPIO_ENABLE 1 + #define GPIO254 0x254 + ++#define HOST_SERIAL_A_HIGH_SPEED (1 << 0) ++#define HOST_SERIAL_B_HIGH_SPEED (1 << 1) ++ + static void sgpio_init(void) + { + uint32_t value; +@@ -310,6 +313,42 @@ void ast_g5_intel_late_init(void) + char buf[32]; + u32 rest = 0; + ++ /* By default host serail A and B use normal speed */ ++ uint32_t host_serial_cfg = 0; ++ char *host_serial_cfg_txt = NULL; ++ ++ /* Config the uart clock source based on environment configuration */ ++ host_serial_cfg_txt = getenv("hostserialcfg"); ++ ++ if (host_serial_cfg_txt != NULL) ++ host_serial_cfg = simple_strtoul(host_serial_cfg_txt, NULL, 16); ++ ++ if (host_serial_cfg > (HOST_SERIAL_A_HIGH_SPEED | HOST_SERIAL_B_HIGH_SPEED)) { ++ printf("Invalided hostserialcfg %x, use default!\n", host_serial_cfg); ++ host_serial_cfg = 0; ++ } ++ ++ /* SOL implementation requires uart1/uart3/uart4 have the same clock ++ * source for data forwarding, config uart3 and uart4 ++ */ ++ if (host_serial_cfg & HOST_SERIAL_A_HIGH_SPEED) { ++ ast_scu_write(ast_scu_read(AST_SCU_MISC2_CTRL) | ++ SCU_UART1_HS_CLOCK | SCU_UART3_HS_CLOCK | ++ SCU_UART4_HS_CLOCK, AST_SCU_MISC2_CTRL); ++ } else { ++ ast_scu_write(ast_scu_read(AST_SCU_MISC2_CTRL) & ++ ~SCU_UART1_HS_CLOCK & ~SCU_UART3_HS_CLOCK & ++ ~SCU_UART4_HS_CLOCK, AST_SCU_MISC2_CTRL); ++ } ++ ++ if (host_serial_cfg & HOST_SERIAL_B_HIGH_SPEED) { ++ ast_scu_write(ast_scu_read(AST_SCU_MISC2_CTRL) | ++ SCU_UART2_HS_CLOCK, AST_SCU_MISC2_CTRL); ++ } else { ++ ast_scu_write(ast_scu_read(AST_SCU_MISC2_CTRL) & ++ ~SCU_UART2_HS_CLOCK, AST_SCU_MISC2_CTRL); ++ } ++ + /* save and clear reset status */ + rest = ast_scu_read(AST_SCU_SYS_CTRL); + snprintf(buf, sizeof(buf), " resetreason=0x%x", rest); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/u-boot-aspeed_%.bbappend b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/u-boot-aspeed_%.bbappend new file mode 100644 index 000000000..9494c823e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-bsp/u-boot/u-boot-aspeed_%.bbappend @@ -0,0 +1,28 @@ +FILESEXTRAPATHS_append_wolfpass:= "${THISDIR}/files:" + +# the meta-phosphor layer adds this patch, which conflicts +# with the intel layout for environment +SRC_URI_remove_wolfpass = " file://0001-configs-ast-Add-redundnant-env.patch" + +SRC_URI_append_wolfpass = " file://0001-flash-use-readX-writeX-not-udelay.patch \ + file://0002-intel-layout-environment-addr.patch \ + file://0004-Make-sure-debug-uart-is-using-24MHz-clock-source.patch \ + file://0005-enable-passthrough-in-uboot.patch \ + file://0006-Add-Aspeed-g5-interrupt-support.patch \ + file://0007-Add-espi-support.patch \ + file://0008-add-sgio-support-for-port80-snoop-post-LEDs.patch \ + file://0009-Add-basic-GPIO-support.patch \ + file://0010-Update-Force-Firmware-Update-Jumper-to-use-new-gpio.patch \ + file://0011-Add-basic-timer-support-for-Aspeed-g5-in-U-Boot.patch \ + file://0012-Add-status-and-ID-LED-support.patch \ + file://0013-aspeed-Add-Pwm-Driver.patch \ + file://0014-Keep-interrupts-enabled-until-last-second.patch \ + file://0015-Rewrite-memmove-to-optimize-on-word-transfers.patch \ + file://0016-Add-support-for-128MB-Macronix-spi-flash-MX66L1G45G.patch \ + file://0017-Enable-Macronix-and-Micron-SPI-support.patch \ + file://0018-Add-support-for-Macronix-and-Micron-1Gbits-SPI-flash.patch \ + file://0019-u-boot-full-platform-reset-espi-oob-ready.patch \ + file://0020-Enable-PCIe-L1-support.patch \ + file://0020-Add-system-reset-status-support.patch \ + file://0021-Config-host-uart-clock-source-using-environment-vari.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/at-scale-debug.bb b/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/at-scale-debug.bb new file mode 100644 index 000000000..8d4a67ec5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/at-scale-debug.bb @@ -0,0 +1,24 @@ +inherit obmc-phosphor-systemd + +SUMMARY = "At Scale Debug Service" +DESCRIPTION = "At Scale Debug Service exposes remote JTAG target debug capabilities" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=40c94c59cbbc218afdd64eec899ad2f6" + +inherit cmake +DEPENDS = "sdbusplus openssl libpam" + +do_configure[depends] += "virtual/kernel:do_shared_workdir" + +SRC_URI = "git://git@github.com/Intel-BMC/at-scale-debug;protocol=ssh" + +SRCREV = "de77d02aefa3c9df62365e7f07224c5cf9b0eb96" +S = "${WORKDIR}/git" + +SYSTEMD_SERVICE_${PN} += "com.intel.AtScaleDebug.service" + +# Specify any options you want to pass to cmake using EXTRA_OECMAKE: +EXTRA_OECMAKE = "-DBUILD_UT=OFF" + +CFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/" diff --git a/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/files/com.intel.AtScaleDebug.service b/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/files/com.intel.AtScaleDebug.service new file mode 100644 index 000000000..65d83b4c4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/files/com.intel.AtScaleDebug.service @@ -0,0 +1,13 @@ +[Unit] +Description=Intel BMC At Scale Debug +Requires=com.intel.AtScaleDebugJtagTest.service network-online.target + +[Service] +Restart=always +RestartSec=30 +ExecStart={bindir}/asd -k /home/root/server.pem +Type=simple +SyslogIdentifier=asd + +[Install] +WantedBy=obmc-host-start@0.target diff --git a/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/files/com.intel.AtScaleDebugJtagTest.service b/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/files/com.intel.AtScaleDebugJtagTest.service new file mode 100644 index 000000000..281d1a993 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/at-scale-debug/files/com.intel.AtScaleDebugJtagTest.service @@ -0,0 +1,12 @@ +[Unit] +Description=Intel BMC At Scale Debug JTAG test to check if remote debug setting is enabled + +[Service] +Type=oneshot +ExecStartPre=/bin/sleep 10 +ExecStart={bindir}/jtag_test +SyslogIdentifier=jtag_test +RemainAfterExit=true + +[Install] +WantedBy=obmc-host-start@0.target diff --git a/meta-openbmc-mods/meta-common/recipes-core/base-files/base-files/fstab b/meta-openbmc-mods/meta-common/recipes-core/base-files/base-files/fstab new file mode 100644 index 000000000..8d65eb990 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/base-files/base-files/fstab @@ -0,0 +1,8 @@ +/dev/root / auto defaults 1 1 +proc /proc proc defaults 0 0 +devpts /dev/pts devpts mode=0620,gid=5 0 0 +tmpfs /run tmpfs mode=0755,nodev,nosuid,strictatime 0 0 + +# uncomment this if your device has a SD/MMC/Transflash slot +#/dev/mmcblk0p1 /media/card auto defaults,sync,noauto 0 0 + diff --git a/meta-openbmc-mods/meta-common/recipes-core/base-files/base-files_%.bbappend b/meta-openbmc-mods/meta-common/recipes-core/base-files/base-files_%.bbappend new file mode 100644 index 000000000..79e529179 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/base-files/base-files_%.bbappend @@ -0,0 +1,2 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +SRC_URI_append = " file://fstab" diff --git a/meta-openbmc-mods/meta-common/recipes-core/bmcweb/bmcweb/0001-Refine-KVM-websock-proxy.patch b/meta-openbmc-mods/meta-common/recipes-core/bmcweb/bmcweb/0001-Refine-KVM-websock-proxy.patch new file mode 100644 index 000000000..acbfebdee --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/bmcweb/bmcweb/0001-Refine-KVM-websock-proxy.patch @@ -0,0 +1,119 @@ +From 430bfc33ee396e4aced0514b66703e5c58a7503d Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 26 Feb 2019 15:46:07 -0800 +Subject: [PATCH] Refine KVM websock proxy + +This commit simplifies input buffer delivering logic by removing +a doWrite() call from readDone(). Input events can be delivered +through websocket's onmessage handler only and it's enough. + +Also, it fixes a suspicious weak point of commit/consume pair on +outputBuffer. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + include/kvm_websocket.hpp | 57 +++++++++++++++++++++-------------------------- + 1 file changed, 26 insertions(+), 31 deletions(-) + +diff --git a/include/kvm_websocket.hpp b/include/kvm_websocket.hpp +index aa2eaecc3205..447171b9981a 100644 +--- a/include/kvm_websocket.hpp ++++ b/include/kvm_websocket.hpp +@@ -20,53 +20,48 @@ static boost::beast::flat_static_buffer<1024U> inputBuffer; + + static crow::websocket::Connection* session = nullptr; + +-static bool doingWrite = false; ++inline void doWrite(); + +-inline void doWrite() ++inline void WriteDone(const boost::system::error_code& ec, ++ std::size_t bytesWritten) + { +- if (doingWrite) ++ BMCWEB_LOG_DEBUG << "Wrote " << bytesWritten << "bytes"; ++ inputBuffer.consume(bytesWritten); ++ ++ if (session == nullptr) ++ { ++ return; ++ } ++ if (ec == boost::asio::error::eof) ++ { ++ session->close("KVM socket port closed"); ++ return; ++ } ++ if (ec) + { +- BMCWEB_LOG_DEBUG << "Already writing. Bailing out"; ++ session->close("Error in reading to host port"); ++ BMCWEB_LOG_ERROR << "Error in KVM socket write " << ec; + return; + } ++ ++ doWrite(); ++} ++ ++inline void doWrite() ++{ + if (inputBuffer.size() == 0) + { + BMCWEB_LOG_DEBUG << "inputBuffer empty. Bailing out"; + return; + } + +- doingWrite = true; +- hostSocket->async_write_some( +- inputBuffer.data(), +- [](boost::beast::error_code ec, std::size_t bytes_written) { +- BMCWEB_LOG_DEBUG << "Wrote " << bytes_written << "bytes"; +- doingWrite = false; +- inputBuffer.consume(bytes_written); +- +- if (session == nullptr) +- { +- return; +- } +- if (ec == boost::asio::error::eof) +- { +- session->close("KVM socket port closed"); +- return; +- } +- if (ec) +- { +- session->close("Error in reading to host port"); +- BMCWEB_LOG_ERROR << "Error in KVM socket write " << ec; +- return; +- } +- doWrite(); +- }); ++ hostSocket->async_write_some(inputBuffer.data(), WriteDone); + } + + inline void doRead(); + + inline void readDone(const boost::system::error_code& ec, std::size_t bytesRead) + { +- outputBuffer.commit(bytesRead); + BMCWEB_LOG_DEBUG << "read done. Read " << bytesRead << " bytes"; + if (ec) + { +@@ -82,6 +77,7 @@ inline void readDone(const boost::system::error_code& ec, std::size_t bytesRead) + return; + } + ++ outputBuffer.commit(bytesRead); + boost::beast::string_view payload( + static_cast<const char*>(outputBuffer.data().data()), bytesRead); + BMCWEB_LOG_DEBUG << "Sending payload size " << payload.size(); +@@ -112,7 +108,6 @@ inline void connectHandler(const boost::system::error_code& ec) + return; + } + +- doWrite(); + doRead(); + } + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-core/bmcweb/bmcweb_%.bbappend b/meta-openbmc-mods/meta-common/recipes-core/bmcweb/bmcweb_%.bbappend new file mode 100644 index 000000000..8d5a43c51 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/bmcweb/bmcweb_%.bbappend @@ -0,0 +1,16 @@ +# add a user called bmcweb for the server to assume +# bmcweb is part of group shadow for non-root pam authentication +USERADD_PARAM_${PN} = "-r -s /usr/sbin/nologin -d /home/bmcweb -m -G shadow bmcweb" + +GROUPADD_PARAM_${PN} = "web; redfish " + +FILESEXTRAPATHS_append := ":${THISDIR}/${PN}" + +SRC_URI += "file://0001-Refine-KVM-websock-proxy.patch" + +# Enable CPU Log and Raw PECI support +EXTRA_OECMAKE += "-DBMCWEB_ENABLE_REDFISH_CPU_LOG=ON" +EXTRA_OECMAKE += "-DBMCWEB_ENABLE_REDFISH_RAW_PECI=ON" + +# Enable Redfish BMC Journal support +EXTRA_OECMAKE += "-DBMCWEB_ENABLE_REDFISH_BMC_JOURNAL=ON" diff --git a/meta-openbmc-mods/meta-common/recipes-core/cpu-log-util/cpu-log-util_git.bb b/meta-openbmc-mods/meta-common/recipes-core/cpu-log-util/cpu-log-util_git.bb new file mode 100644 index 000000000..10793689b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/cpu-log-util/cpu-log-util_git.bb @@ -0,0 +1,27 @@ +inherit obmc-phosphor-dbus-service +inherit obmc-phosphor-systemd + +SUMMARY = "CPU Log Utils" +DESCRIPTION = "CPU utilities for dumping CPU registers over PECI" + +DEPENDS = "boost cjson sdbusplus " +inherit cmake + +LICENSE = "CLOSED" +LIC_FILES_CHKSUM = "" + +SRC_URI = "git://git@github.com/Intel-BMC/at-scale-debug;protocol=ssh" +SRCREV = "de77d02aefa3c9df62365e7f07224c5cf9b0eb96" + +S = "${WORKDIR}/git/cpu-log-util" +PACKAGES += "libpeci" + +SYSTEMD_SERVICE_${PN} += "com.intel.CpuDebugLog.service" +DBUS_SERVICE_${PN} += "com.intel.CpuDebugLog.service" + +# linux-libc-headers guides this way to include custom uapi headers +CFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/uapi" +CFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include" +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/uapi" +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include" +do_configure[depends] += "virtual/kernel:do_shared_workdir" diff --git a/meta-openbmc-mods/meta-common/recipes-core/cpu-log-util/files/com.intel.CpuDebugLog.service b/meta-openbmc-mods/meta-common/recipes-core/cpu-log-util/files/com.intel.CpuDebugLog.service new file mode 100644 index 000000000..13d2c860e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/cpu-log-util/files/com.intel.CpuDebugLog.service @@ -0,0 +1,10 @@ +[Unit] +Description=Intel BMC CPU Debug Log + +[Service] +Restart=always +ExecStart={bindir}/cpu_log +Type=simple + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh b/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh new file mode 100644 index 000000000..5a2637ba3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh @@ -0,0 +1,104 @@ +#!/bin/sh + +SSH_ID=$HOME/.ssh/id_rsa.db +[ -e $HOME/.fwupd.defaults ] && source $HOME/.fwupd.defaults + +usage() { + echo "usage: $(basename $0) uri" + echo " uri is something like: file:///path/to/fw" + echo " tftp://tftp.server.ip.addr/path/to/fw" + echo " scp://[user@]scp.server.ip.addr:/path/to/fw" + echo " http[s]://web.server.ip.addr/path/to/fw" + echo " ftp://[user@]ftp.server.ip.addr/path/to/fw" + exit 1 +} + +if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then usage; fi +if [ $# -eq 0 ]; then + # set DEFURI in $HOME/.fwupd.defaults + URI="$DEFURI" +else + URI="$1" +fi + +PROTO=$(echo "$URI" | sed 's,\([a-z]*\)://.*$,\1,') +REMOTE=$(echo "$URI" | sed 's,.*://\(.*\)$,\1,') +REMOTE_HOST=$(echo "$REMOTE" | sed 's,\([^/]*\)/.*$,\1,') +if [ "$PROTO" = 'scp' ]; then + REMOTE_PATH=$(echo "$REMOTE" | cut -d':' -f2) +else + REMOTE_PATH=$(echo "$REMOTE" | sed 's,[^/]*/\(.*\)$,\1,') +fi +LOCAL_PATH="/tmp/$(basename $REMOTE_PATH)" +echo "PROTO=$PROTO" +echo "REMOTE=$REMOTE" +echo "REMOTE_HOST=$REMOTE_HOST" +echo "REMOTE_PATH=$REMOTE_PATH" +if [ ! -e $LOCAL_PATH ] || [ $(stat -c %s $LOCAL_PATH) -eq 0 ]; then + echo "Download '$REMOTE_PATH' from $PROTO $REMOTE_HOST $REMOTE_PATH" + case "$PROTO" in + scp) + mkdir -p $HOME/.ssh + if [ -e "$SSH_ID" ]; then + ARG_ID="-i $SSH_ID" + fi + scp $ARG_ID $REMOTE_HOST$REMOTE_PATH $LOCAL_PATH + if [ $? -ne 0 ]; then + echo "scp $REMOTE $LOCAL_PATH failed!" + exit 255 + fi + ;; + tftp) + cd /tmp + tftp -g -r "$REMOTE_PATH" "$REMOTE_HOST" + if [ $? -ne 0 ]; then + echo "tftp -g -r \"$REMOTE_PATH\" \"$REMOTE_HOST\" failed!" + exit 255 + fi + ;; + http|https|ftp) + wget --no-check-certificate "$URI" -O "$LOCAL_PATH" + if [ $? -ne 0 ]; then + echo "wget $URI failed!" + exit 255 + fi + ;; + file) + cp "$REMOTE_PATH" "$LOCAL_PATH" + ;; + *) + echo "Invalid URI $URI" + exit 1; + ;; + esac +fi + +# do a quick sanity check on the image +if [ $(stat -c "%s" "$LOCAL_PATH") -lt 10000000 ]; then + echo "Update file "$LOCAL_PATH" seems to be too small" + exit 1 +fi +dtc -I dtb -O dtb "$LOCAL_PATH" > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "Update file $LOCAL_PATH doesn't seem to be in the proper format" + exit 1 +fi + +# guess based on fw_env which partition we booted from +BOOTADDR=$(fw_printenv bootcmd | awk '{print $2}') + +TGT="/dev/mtd/image-a" +case "$BOOTADDR" in + 20080000) TGT="/dev/mtd/image-b"; BOOTADDR="22480000" ;; + 22480000) TGT="/dev/mtd/image-a"; BOOTADDR="20080000" ;; + *) TGT="/dev/mtd/image-a"; BOOTADDR="20080000" ;; +esac +echo "Updating $(basename $TGT) (use bootm $BOOTADDR)" +flash_erase $TGT 0 0 +echo "Writing $(stat -c "%s" "$LOCAL_PATH") bytes" +cat "$LOCAL_PATH" > "$TGT" +fw_setenv "bootcmd" "bootm ${BOOTADDR}" + +# reboot +reboot + diff --git a/meta-openbmc-mods/meta-common/recipes-core/fw-update/intel-fw-update.bb b/meta-openbmc-mods/meta-common/recipes-core/fw-update/intel-fw-update.bb new file mode 100644 index 000000000..ea39e62be --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/fw-update/intel-fw-update.bb @@ -0,0 +1,18 @@ +SUMMARY = "Temporary intel-fw-update script" +DESCRIPTION = "At runtime, perform a firmware update and reboot" +PR = "r1" + +# flash_eraseall +RDEPENDS_intel-fw-update += "mtd-utils" +# wget tftp scp +RDEPENDS_intel-fw-update += "busybox dropbear" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SRC_URI += "file://fwupd.sh" + +do_install() { + install -d ${D}${bindir} + install -m 0755 ${WORKDIR}/fwupd.sh ${D}${bindir} +} diff --git a/meta-openbmc-mods/meta-common/recipes-core/ipmi/intel-ipmi-oem_%.bbappend b/meta-openbmc-mods/meta-common/recipes-core/ipmi/intel-ipmi-oem_%.bbappend new file mode 100644 index 000000000..a7c19f708 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/ipmi/intel-ipmi-oem_%.bbappend @@ -0,0 +1,2 @@ +SRC_URI = "git://github.com/openbmc/intel-ipmi-oem.git" +SRCREV = "fcce83df799d9580f48b7f793989c9c96bc882e0" diff --git a/meta-openbmc-mods/meta-common/recipes-core/ipmi/ipmi-providers.bb b/meta-openbmc-mods/meta-common/recipes-core/ipmi/ipmi-providers.bb new file mode 100644 index 000000000..9e620a19c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/ipmi/ipmi-providers.bb @@ -0,0 +1,37 @@ +SUMMARY = "Intel IPMI Providers" +DESCRIPTION = "IPMI Provider Libraries" + +SRC_URI = "git://git@github.com/Intel-BMC/intel-ipmi-providers;protocol=ssh" +SRCREV = "3573b25576d14b3334f93bd988c6e2003fab8f90" + +S = "${WORKDIR}/git" +PV = "0.1+git${SRCPV}" + +DEPENDS = "boost phosphor-ipmi-host intel-ipmi-oem systemd microsoft-gsl" + +inherit cmake obmc-phosphor-ipmiprovider-symlink + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=2ee41112a44fe7014dce33e26468ba93" + +EXTRA_OECMAKE="-DENABLE_TEST=0 -DYOCTO=1" + +LIBRARY_NAMES += "libmtmcmds.so" +LIBRARY_NAMES += "libsmbioshandler.so" +LIBRARY_NAMES += "libzbridgecmd.so" +LIBRARY_NAMES += "libsmbiosmdrv2.so" +LIBRARY_NAMES += "libfwupdcmds.so" + +HOSTIPMI_PROVIDER_LIBRARY += "${LIBRARY_NAMES}" +NETIPMI_PROVIDER_LIBRARY += "${LIBRARY_NAMES}" + +FILES_${PN}_append = " ${libdir}/ipmid-providers/lib*${SOLIBS}" +FILES_${PN}_append = " ${libdir}/host-ipmid/lib*${SOLIBS}" +FILES_${PN}_append = " ${libdir}/net-ipmid/lib*${SOLIBS}" +FILES_${PN}-dev_append = " ${libdir}/ipmid-providers/lib*${SOLIBSDEV}" + +do_configure_prepend() { + cp -r ${WORKDIR}/recipe-sysroot${libdir}/phosphor-ipmi-host ${S} + cp -r ${WORKDIR}/recipe-sysroot${includedir}/phosphor-ipmi-host ${S} + cp -r ${WORKDIR}/recipe-sysroot${includedir}/intel-ipmi-oem ${S} +} diff --git a/meta-openbmc-mods/meta-common/recipes-core/microsoft-gsl/microsoft-gsl.bb b/meta-openbmc-mods/meta-common/recipes-core/microsoft-gsl/microsoft-gsl.bb new file mode 100644 index 000000000..8cb593bce --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/microsoft-gsl/microsoft-gsl.bb @@ -0,0 +1,24 @@ +# Add GSL: Guideline Support Library for c++ +# https://github.com/Microsoft/GSL + +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://LICENSE;md5=363055e71e77071107ba2bb9a54bd9a7" + +SRC_URI = "git://github.com/Microsoft/GSL.git;protocol=https" + +# Modify these as desired +PV = "1.0+git${SRCPV}" +#SRCREV = "${AUTOREV}" +SRCREV = "be43c79742dc36ee55b21c5d531a5ff301d0ef8d" + +S = "${WORKDIR}/git" + +do_install () { + install -d ${D}/usr/include + install -d ${D}/usr/include/gsl + for F in ${S}/include/gsl/*; do + install -m 0644 ${F} ${D}/usr/include/gsl + done +} + +ALLOW_EMPTY_${PN} = "1" diff --git a/meta-openbmc-mods/meta-common/recipes-core/nv-overlay/files/nv-overlay.service b/meta-openbmc-mods/meta-common/recipes-core/nv-overlay/files/nv-overlay.service new file mode 100644 index 000000000..95957591f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/nv-overlay/files/nv-overlay.service @@ -0,0 +1,15 @@ +[Unit] +Description=Non-volatile overlay + +Before=sysinit.target systemd-sysctl.service systemd-modules-load.service swap.target systemd-tmpfiles-setup.service +DefaultDependencies=no +Conflicts=shutdown.target +ConditionFileNotEmpty={bindir}/nv-overlay.sh + +[Service] +Type=oneshot +ExecStart={bindir}/nv-overlay.sh + +[Install] +RequiredBy=paths.target + diff --git a/meta-openbmc-mods/meta-common/recipes-core/nv-overlay/files/nv-overlay.sh b/meta-openbmc-mods/meta-common/recipes-core/nv-overlay/files/nv-overlay.sh new file mode 100755 index 000000000..cde082013 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/nv-overlay/files/nv-overlay.sh @@ -0,0 +1,141 @@ +#!/bin/sh + +# Copyright 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# provide a couple of places in the RO root filesystem +# that can be made RW with an overlayfs + +# start with /proc and /tmp mounted +[ -e /proc/mounts ] || mount -t proc proc /proc +grep -q /tmp /proc/mounts || mount -t tmpfs -o rw,nosuid,nodev tmp /tmp + +# list of things that need to be rw at boot +NV_OVERLAYS="/etc /var /home" +TMP_FS="/var/log /var/lib/systemd/coredump /media" + +# place to mount the real jffs2 backing store +RWFS_MNT=/tmp/.rwfs + +if grep -q "$RWFS_MNT" /proc/mounts; then + # quit - we have already run + exit 0 +fi +mkdir -p "$RWFS_MNT" + +mtd_by_name() { + local name="$1" + local mtd="/dev/$(grep "$name" /proc/mtd | cut -d : -f 1)" + echo "$mtd" +} + +mtdblock_by_name() { + local name="$1" + local mtdblock="$(mtd_by_name "$name" | sed 's,mtd,mtdblock,')" + echo "$mtdblock" +} + +NV_MTD=rwfs +NV_MTD_DEV="$(mtd_by_name ${NV_MTD})" +NV_MTD_BLOCKDEV="$(mtdblock_by_name ${NV_MTD})" + +nvrw() { + local p="$1" + mkdir -p "${RWFS_MNT}${p}" "${RWFS_MNT}${p}.work" + local mname=$(echo "rwnv${p}" | sed 's,/,,g') + local opts="lowerdir=${p},upperdir=${RWFS_MNT}${p},workdir=${RWFS_MNT}${p}.work" + mount -t overlay -o "$opts" "$mname" "$p" +} + +targetted_clean() { + local LOG_TAG="restore-defaults" + # Do not delete server certificates for the web server or ssh + echo "removing targetted contents:" + cd "${RWFS_MNT}/etc" + for file in *; do + case $file in + # The only files that stay are here: + CA|RestoreDefaultConfiguration|dropbear|sdr|server.pem);; + # All else get removed. + *) echo "remove $file" + rm -rf $file;; + esac + done + # nothing should be in the workdir, but clear it just in case + rm -rf "${RWFS_MNT}/etc.work" + + # Log files remaining - but not to stdout. + echo "Files remaining: $(ls)" + + # clean everything out of /var + rm -rf "${RWFS_MNT}/var" "${RWFS_MNT}/var.work" +} + +full_clean() { + local OVL='' + for OVL in $NV_OVERLAYS; do + rm -rf "${RWFS_MNT}${OVL}" "${RWFS_MNT}${OVL}.work" + done +} + +# check for full factory reset: if so, flash_eraseall $NV_MTD_DEV +bootflags="0x$(sed 's/^.*bootflags=\([0-9a-f]*\).*$/\1/' /proc/cmdline)" +let "restore_op = $bootflags & 0x3" +if [ $restore_op -eq 3 ]; then + flash_eraseall "$NV_MTD_DEV" +fi + +mount -t jffs2 "$NV_MTD_BLOCKDEV" "$RWFS_MNT" + +if [ $restore_op -eq 1 ]; then + targetted_clean +elif [ $restore_op -eq 2 ]; then + full_clean +fi + +for FS in $NV_OVERLAYS; do + nvrw "$FS" +done + +for FS in $TMP_FS; do + mount -t tmpfs tmpfs "$FS" +done + +# make sure that /etc/fw_env.config mirrors our current uboot environment +UENV_MTD_INFO=$(grep UENV /proc/mtd) +if [ -n "$UENV_MTD_INFO" ]; then + UENV_MTD_INFO=$(echo "$UENV_MTD_INFO" | sed 's,^\([^:]*\): \([0-9a-f]*\) \([0-9a-f]*\) .*,/dev/\1 0 0x\2 0x\3,') + if ! grep -q "^${UENV_MTD_INFO}$" /etc/fw_env.config; then + echo "${UENV_MTD_INFO}" > /etc/fw_env.config + echo "Updated fw_env.config" + fi +fi + +# work around bug where /etc/machine-id will be mounted with a temporary file +# if rootfs is read-only and the file is empty +MACHINE_ID=/etc/machine-id +if [ ! -s "$MACHINE_ID" ]; then + systemd-machine-id-setup +fi + +# mount persistent NV filesystem, where immortal settings live +if ! grep -q sofs /proc/mounts; then + mkdir -p /var/sofs + SOFS_MTD=sofs + SOFS_MTD_BLOCKDEV="$(mtdblock_by_name ${SOFS_MTD})" + mount -t jffs2 "$SOFS_MTD_BLOCKDEV" /var/sofs +fi + +echo "Finished mounting non-volatile overlays" diff --git a/meta-openbmc-mods/meta-common/recipes-core/nv-overlay/nv-overlay.bb b/meta-openbmc-mods/meta-common/recipes-core/nv-overlay/nv-overlay.bb new file mode 100644 index 000000000..78b0f80ca --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/nv-overlay/nv-overlay.bb @@ -0,0 +1,19 @@ +SUMMARY = "Limited NV overlay init script" +DESCRIPTION = "At runtime, overlay a few directories with an NV COW" +PR = "r1" + +inherit obmc-phosphor-systemd + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SRC_URI += "file://nv-overlay.sh" +SRC_URI += "file://nv-overlay.service" + +do_install_append() { + install -d ${D}${bindir} + install -m 0755 ${WORKDIR}/nv-overlay.sh ${D}${bindir} +} + +TMPL = "nv-overlay.service" +SYSTEMD_SERVICE_${PN} += "${TMPL}" diff --git a/meta-openbmc-mods/meta-common/recipes-core/os-release/os-release.bbappend b/meta-openbmc-mods/meta-common/recipes-core/os-release/os-release.bbappend new file mode 100644 index 000000000..584d3b645 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/os-release/os-release.bbappend @@ -0,0 +1,85 @@ +# WARNING! +# +# These modifications to os-release disable the bitbake parse +# cache (for the os-release recipe only). Before copying +# and pasting into another recipe ensure it is understood +# what that means! + +def irun_git(d, oeroot, git_cmd, **kwargs): + err = None + try: + cmd = 'git --work-tree {} --git-dir {}/.git {}'.format(oeroot, oeroot, git_cmd) + ret, err = bb.process.run(cmd, **kwargs) + if err is not None: + ret += err + except bb.process.ExecutionError as e: + ret = '' + if e.stdout is not None: + ret += e.stdout + if e.stderr is not None: + ret += e.stderr + except Exception as e: + ret = str(e) + return ret.strip('\n') + +def repo_status(d, f, repo, tagargs): + import subprocess + + cmd_list = [['HEAD', 'rev-parse HEAD'], + ['TAG', 'describe {} --dirty --long'.format(tagargs)], + ['STATUS', 'status -sb']] + + f.write(('\n# REPOSITORY: {} '.format(os.path.basename(repo))).ljust(80, '+') + '\n') + for item in cmd_list: + f.write('# {}: '.format(item[0])) + sb = irun_git(d, repo, item[1]) + if sb: + sb_lines = sb.split('\n') + if len(sb_lines) == 1: + f.write(sb_lines[0]) + else: + f.write('\n# ' + '\n# '.join(sb_lines)) + f.write('\n') + +python() { + corebase = d.getVar('COREBASE', True) + mibase = os.path.join(corebase, 'meta-openbmc-mods') + obmc_vers = irun_git(d, corebase, 'describe --dirty --long') + meta_vers = irun_git(d, mibase, 'rev-parse HEAD')[0:7] + version_id = '{}-{}'.format(obmc_vers, meta_vers) + if version_id: + d.setVar('VERSION_ID', version_id) + versionList = version_id.split('-') + version = '{}-{}'.format(versionList[0], versionList[1]) + d.setVar('VERSION', version) + + build_id = irun_git(d, corebase, 'describe --abbrev=0') + if build_id: + d.setVar('BUILD_ID', build_id) +} + +OS_RELEASE_FIELDS_append = " BUILD_ID" + +python do_compile_append () { + import glob + with open(d.expand('${B}/os-release'), 'a') as f: + corebase = d.getVar('COREBASE', True) + f.write('\n# Build Configuration Details\n') + repo_status(d, f, corebase, '') + repo_status(d, f, os.path.join(corebase, 'meta-openbmc-mods'), '--tags') + appends_dir = os.path.join(d.getVar('TOPDIR', True), 'workspace', 'appends') + + for fn in glob.glob(os.path.join(appends_dir, '*.bbappend')): + with open(fn, 'r') as bb_f: + for line in bb_f: + if line.startswith('# srctreebase: '): + srctreebase = line.split(':', 1)[1].strip() + repo_status(d, f, srctreebase, '--tags') +} + + +# Ensure the git commands run every time bitbake is invoked. +BB_DONT_CACHE = "1" + +# Make os-release available to other recipes. +SYSROOT_DIRS_append = " ${sysconfdir}" diff --git a/meta-openbmc-mods/meta-common/recipes-core/packagegroups/packagegroup-core-standalone-sdk-target.bbappend b/meta-openbmc-mods/meta-common/recipes-core/packagegroups/packagegroup-core-standalone-sdk-target.bbappend new file mode 100644 index 000000000..87a4c8503 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/packagegroups/packagegroup-core-standalone-sdk-target.bbappend @@ -0,0 +1 @@ +RRECOMMENDS_${PN}_append = " vim cmake sdbusplus" diff --git a/meta-openbmc-mods/meta-common/recipes-core/peci-pcie/peci-pcie_git.bb b/meta-openbmc-mods/meta-common/recipes-core/peci-pcie/peci-pcie_git.bb new file mode 100644 index 000000000..91eed5a82 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/peci-pcie/peci-pcie_git.bb @@ -0,0 +1,24 @@ +# NOTE: LICENSE is being set to "CLOSED" for now. The PCIe reads over PECI expose +# more information than is accessible from the BIOS or OS, so we need to keep this +# internal to Intel until it's resolved. +LICENSE = "CLOSED" +LIC_FILES_CHKSUM = "" +inherit cmake systemd + +SRC_URI = "git://git@github.com/Intel-BMC/provingground;protocol=ssh" + +DEPENDS = "boost sdbusplus cpu-log-util" + +PV = "0.1+git${SRCPV}" +SRCREV = "3cc86d6c536b4c5ee7afb5447837b83ce8b3d149" + +S = "${WORKDIR}/git/peci_pcie" + +SYSTEMD_SERVICE_${PN} += "xyz.openbmc_project.PCIe.service" + +# linux-libc-headers guides this way to include custom uapi headers +CFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/uapi" +CFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include" +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/uapi" +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include" +do_configure[depends] += "virtual/kernel:do_shared_workdir" diff --git a/meta-openbmc-mods/meta-common/recipes-core/readline/readline/inputrc b/meta-openbmc-mods/meta-common/recipes-core/readline/readline/inputrc new file mode 100644 index 000000000..7b84c9916 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/readline/readline/inputrc @@ -0,0 +1,61 @@ +# /etc/inputrc - global inputrc for libreadline +# See readline(3readline) and `info rluserman' for more information. + +# Be 8 bit clean. +set input-meta on +set output-meta on + +# To allow the use of 8bit-characters like the german umlauts, comment out +# the line below. However this makes the meta key not work as a meta key, +# which is annoying to those which don't need to type in 8-bit characters. + +# set convert-meta off + +# try to enable the application keypad when it is called. Some systems +# need this to enable the arrow keys. +# set enable-keypad on + +# see /usr/share/doc/bash/inputrc.arrows for other codes of arrow keys + +# do not bell on tab-completion +# set bell-style none + +# some defaults / modifications for the emacs mode +$if mode=emacs + +# allow the use of the Home/End keys + "\e[1~": beginning-of-line + "\e[4~": end-of-line + +# allow the use of the Delete/Insert keys + "\e[3~": delete-char +# "\e[2~": quoted-insert + +# mappings for "page up" and "page down" to step to the beginning/end +# of the history +# "\e[5~": beginning-of-history +# "\e[6~": end-of-history + +# alternate mappings for "page up" and "page down" to search the history +# "\e[5~": history-search-backward +# "\e[6~": history-search-forward + +# # mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving +# "\e[5C": forward-word +# "\e[5D": backward-word +# "\e\e[C": forward-word +# "\e\e[D": backward-word + +# $if term=rxvt +# "\e[8~": end-of-line +# $endif + +# for non RH/Debian xterm, can't hurt for RH/DEbian xterm +# "\eOH": beginning-of-line +# "\eOF": end-of-line + +# for freebsd console +# "\e[H": beginning-of-line +# "\e[F": end-of-line + +$endif diff --git a/meta-openbmc-mods/meta-common/recipes-core/readline/readline_%.bbappend b/meta-openbmc-mods/meta-common/recipes-core/readline/readline_%.bbappend new file mode 100644 index 000000000..c63a45dd4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/readline/readline_%.bbappend @@ -0,0 +1,2 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +SRC_URI_append = " file://inputrc" diff --git a/meta-openbmc-mods/meta-common/recipes-core/rest-dbus/rest-dbus-static.bb b/meta-openbmc-mods/meta-common/recipes-core/rest-dbus/rest-dbus-static.bb new file mode 100644 index 000000000..429d5b4b4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/rest-dbus/rest-dbus-static.bb @@ -0,0 +1,23 @@ +SUMMARY = "Phosphor OpenBMC REST framework" +DESCRIPTION = "Phosphor OpenBMC REST to DBUS daemon." +HOMEPAGE = "http://github.com/openbmc/rest-dbus" +PR = "r1" + +inherit allarch +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SRC_URI += "git://github.com/openbmc/rest-dbus.git" + +SRCREV = "9273a302e8f2b3c3e939dff77758e90f163bf6a1" + +S = "${WORKDIR}/git" + +FILES_${PN} += "${datadir}/www/rest-dbus/*" + +do_install () { + install -d ${D}${datadir}/www/rest-dbus/res + install -m 644 ${S}/resources/** ${D}${datadir}/www/rest-dbus/res + install -m 644 ${S}/resources/index.html ${D}${datadir}/www/rest-dbus/index.html +} + diff --git a/meta-openbmc-mods/meta-common/recipes-core/systemd/obmc-targets.bbappend b/meta-openbmc-mods/meta-common/recipes-core/systemd/obmc-targets.bbappend new file mode 100644 index 000000000..3fe1c3f38 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/systemd/obmc-targets.bbappend @@ -0,0 +1,9 @@ +# Remove these files since they are provided by obmc-intel-targets +SYSTEMD_SERVICE_${PN}_remove += " obmc-host-start@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-host-stop@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-host-reboot@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-host-startmin@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-chassis-poweron@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-chassis-poweroff@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-chassis-hard-poweroff@.target" +SYSTEMD_SERVICE_${PN}_remove += " obmc-chassis-powerreset@.target" diff --git a/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd/000-ro-rootfs-tmpfile-defaults.patch b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd/000-ro-rootfs-tmpfile-defaults.patch new file mode 100644 index 000000000..d16f3a2dc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd/000-ro-rootfs-tmpfile-defaults.patch @@ -0,0 +1,51 @@ +From 05e1b853abfd54d117dad25185c602d1791d83f6 Mon Sep 17 00:00:00 2001 +From: David Cobbley <david.j.cobbley@linux.intel.com> +Date: Tue, 26 Jun 2018 16:10:14 -0700 +Subject: [PATCH] ro-rootfs-tmpfile-defaults + +--- + tmpfiles.d/home.conf | 1 - + tmpfiles.d/tmp.conf | 1 - + tmpfiles.d/var.conf.m4 | 5 ++++- + 3 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/tmpfiles.d/home.conf b/tmpfiles.d/home.conf +index 9f25b83..5c7513a 100644 +--- a/tmpfiles.d/home.conf ++++ b/tmpfiles.d/home.conf +@@ -8,4 +8,3 @@ + # See tmpfiles.d(5) for details + + Q /home 0755 - - - +-q /srv 0755 - - - +diff --git a/tmpfiles.d/tmp.conf b/tmpfiles.d/tmp.conf +index 22555a0..aad1b98 100644 +--- a/tmpfiles.d/tmp.conf ++++ b/tmpfiles.d/tmp.conf +@@ -9,7 +9,6 @@ + + # Clear tmp directories separately, to make them easier to override + q /tmp 1777 root root 10d +-q /var/tmp 1777 root root 30d + + # Exclude namespace mountpoints created with PrivateTmp=yes + x /tmp/systemd-private-%b-* +diff --git a/tmpfiles.d/var.conf.m4 b/tmpfiles.d/var.conf.m4 +index 0e2c509..fa288b8 100644 +--- a/tmpfiles.d/var.conf.m4 ++++ b/tmpfiles.d/var.conf.m4 +@@ -11,7 +11,10 @@ q /var 0755 - - - + + L /var/run - - - - ../run + +-d /var/log 0755 - - - ++# now /var/log and /var/tmp really live in volatile ++L /var/log - - - - volatile/log ++L /var/tmp - - - - volatile/tmp ++ + m4_ifdef(`ENABLE_UTMP', + f /var/log/wtmp 0664 root utmp - + f /var/log/btmp 0660 root utmp - +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd/0001-Modfiy-system.conf-DefaultTimeoutStopSec.patch b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd/0001-Modfiy-system.conf-DefaultTimeoutStopSec.patch new file mode 100644 index 000000000..f72052e0c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd/0001-Modfiy-system.conf-DefaultTimeoutStopSec.patch @@ -0,0 +1,28 @@ +From 3016898f4300fdd8db74f821cd6ea54dbf39fdc8 Mon Sep 17 00:00:00 2001 +From: James Feist <james.feist@linux.intel.com> +Date: Tue, 6 Mar 2018 16:06:33 -0800 +Subject: [PATCH 1/1] Modfiy system.conf DefaultTimeoutStopSec + +Current time is 5 minutes, change it to 10 seconds. + +Signed-off-by: James Feist <james.feist@linux.intel.com> +--- + src/core/system.conf | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/system.conf.in b/src/core/system.conf.in +index 746572b..ba2a265 100644 +--- a/src/core/system.conf.in ++++ b/src/core/system.conf.in +@@ -33,7 +33,7 @@ + #DefaultStandardOutput=journal + #DefaultStandardError=inherit + #DefaultTimeoutStartSec=90s +-#DefaultTimeoutStopSec=90s ++DefaultTimeoutStopSec=10s + #DefaultRestartSec=100ms + #DefaultStartLimitIntervalSec=10s + #DefaultStartLimitBurst=5 +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd_%.bbappend b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd_%.bbappend new file mode 100644 index 000000000..ef7ac20e2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/systemd/systemd_%.bbappend @@ -0,0 +1,11 @@ +# add some configuration overrides for systemd default /usr/lib/tmpfiles.d/ + +LICENSE = "GPL-2.0" + +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://000-ro-rootfs-tmpfile-defaults.patch \ + file://0001-Modfiy-system.conf-DefaultTimeoutStopSec.patch \ + " + +USERADD_PACKAGES_remove = "${PN}-journal-gateway ${PN}-journal-upload ${PN}-journal-remote" diff --git a/meta-openbmc-mods/meta-common/recipes-devtools/dtoverlay/dtoverlay.bb b/meta-openbmc-mods/meta-common/recipes-devtools/dtoverlay/dtoverlay.bb new file mode 100644 index 000000000..66107d81b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-devtools/dtoverlay/dtoverlay.bb @@ -0,0 +1,31 @@ +SUMMARY = "dtoverlay" +DESCRIPTION = "device tree overlay application" + +SRC_URI = "git://github.com/raspberrypi/userland.git" +LICENSE = "BSD" +LIC_FILES_CHKSUM = "file://LICENCE;md5=0448d6488ef8cc380632b1569ee6d196" + +SRCREV = "11389772c79685442e0ab8aa9be8ad0e32703f68" +requires = "chrpath-native" + +S = "${WORKDIR}/git" + +PV = "1" + +inherit cmake + +FILES_${PN} += "${libdir} ${libdir}libdtovl.so.${PV}" + +do_compile_append(){ + chrpath -d ${S}/build/bin/dtoverlay +} + +do_install() { + install -d ${D}${libdir} + install -m 0644 ${S}/build/lib/libdtovl.so ${D}${libdir}/libdtovl.so.${PV} + install -d ${D}${bindir} + install -m 0755 ${S}/build/bin/dtoverlay ${D}${bindir}/dtoverlay + + ln -sf libdtovl.so.${PV} ${D}{libdir}libdtovl.so +} + diff --git a/meta-openbmc-mods/meta-common/recipes-devtools/mtd-util/mtd-util.bb b/meta-openbmc-mods/meta-common/recipes-devtools/mtd-util/mtd-util.bb new file mode 100644 index 000000000..f973acac6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-devtools/mtd-util/mtd-util.bb @@ -0,0 +1,20 @@ +DESCRIPTION = "OpenBMC mtd-util" + +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://COPYING;md5=b77c43ae4eaf67bd73fb6452b2f113a3" + +SRC_URI = "git://git@github.com/Intel-BMC/mtd-util;protocol=ssh" + +PV = "1.0+git${SRCPV}" +SRCREV = "22a0216b5e197bb8c7264fd0be0a4bb2d5d25b90" + + +S = "${WORKDIR}/git" + +DEPENDS += "dbus openssl zlib boost microsoft-gsl" + +inherit cmake pkgconfig + +# Specify any options you want to pass to cmake using EXTRA_OECMAKE: +EXTRA_OECMAKE = "" + diff --git a/meta-openbmc-mods/meta-common/recipes-devtools/template-recipe/template-recipe.bb b/meta-openbmc-mods/meta-common/recipes-devtools/template-recipe/template-recipe.bb new file mode 100644 index 000000000..5d13a8c44 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-devtools/template-recipe/template-recipe.bb @@ -0,0 +1,18 @@ +SUMMARY = "HelloWorld app with phosphor-logging usage example." +DESCRIPTION = "NOTE: Phosphor-logging has dependencies on systemd and sdbusplus." + +SRC_URI = "git://git-amr-2.devtools.intel.com:29418/openbmc_template-recipe;protocol=ssh" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +# Modify these as desired +PV = "1.0+git${SRCPV}" +SRCREV = "2d5d731254319de8b42d6438b0ce3908dd5b0dec" + +S = "${WORKDIR}/git" + +inherit cmake + +DEPENDS = "systemd sdbusplus phosphor-logging" +RDEPENDS_${PN} = "libsystemd sdbusplus phosphor-logging" diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/libvncserver/libvncserver_%.bbappend b/meta-openbmc-mods/meta-common/recipes-graphics/libvncserver/libvncserver_%.bbappend new file mode 100644 index 000000000..ddbb2d7ae --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/libvncserver/libvncserver_%.bbappend @@ -0,0 +1,21 @@ +PACKAGECONFIG_remove = "gcrypt gnutls png sdl zlib" + +TARGET_CXXFLAGS += " -Dflto" + +do_install_append() { + rm -rf ${D}${libdir}/libvncclient* +} + +inherit cmake + +# Use the latest to support obmc-ikvm +DEPENDS += "openssl" +SRC_URI = "git://github.com/LibVNC/libvncserver" +SRCREV = "3348a7e42e86dfb98dd7458ad29def476cf6096f" +S = "${WORKDIR}/git" + +# Remove x11 and gtk+ that cause big image size +# Actually, these aren't needed to support obmc-ikvm +REQUIRED_DISTRO_FEATURES_remove = "x11" +DEPENDS_remove = "gtk+" +RDEPENDS_${PN}_remove = "gtk+" diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/.clang-format b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/.clang-format new file mode 100644 index 000000000..8c5278e6f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/.clang-format @@ -0,0 +1,98 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^[<"](gtest|gmock)' + Priority: 5 + - Regex: '^"config.h"' + Priority: -1 + - Regex: '^".*\.hpp"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 + - Regex: '^<.*' + Priority: 3 + - Regex: '.*' + Priority: 4 +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/LICENSE b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/MAINTAINERS b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/MAINTAINERS new file mode 100644 index 000000000..a5ab97e02 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/MAINTAINERS @@ -0,0 +1,45 @@ +How to use this list: + Find the most specific section entry (described below) that matches where + your change lives and add the reviewers (R) and maintainers (M) as + reviewers. You can use the same method to track down who knows a particular + code base best. + + Your change/query may span multiple entries; that is okay. + + If you do not find an entry that describes your request at all, someone + forgot to update this list; please at least file an issue or send an email + to a maintainer, but preferably you should just update this document. + +Description of section entries: + + Section entries are structured according to the following scheme: + + X: NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!> + X: ... + . + . + . + + Where REPO_NAME is the name of the repository within the OpenBMC GitHub + organization; FILE_PATH is a file path within the repository, possibly with + wildcards; X is a tag of one of the following types: + + M: Denotes maintainer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>; + if omitted from an entry, assume one of the maintainers from the + MAINTAINERS entry. + R: Denotes reviewer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>; + these people are to be added as reviewers for a change matching the repo + path. + F: Denotes forked from an external repository; has fields URL. + + Line comments are to be denoted "# SOME COMMENT" (typical shell style + comment); it is important to follow the correct syntax and semantics as we + may want to use automated tools with this file in the future. + + A change cannot be added to an OpenBMC repository without a MAINTAINER's + approval; thus, a MAINTAINER should always be listed as a reviewer. + +START OF MAINTAINERS LIST +------------------------- + +M: Eddie James <eajames@linux.ibm.com> <eajames!> diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/Makefile.am b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/Makefile.am new file mode 100644 index 000000000..1022b2e59 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/Makefile.am @@ -0,0 +1,31 @@ +bin_PROGRAMS = obmc-ikvm +dist_bin_SCRIPTS = create_usbhid.sh + +noinst_HEADERS = \ + ikvm_args.hpp \ + ikvm_input.hpp \ + ikvm_manager.hpp \ + ikvm_server.hpp \ + ikvm_video.hpp + +obmc_ikvm_SOURCES = \ + ikvm_args.cpp \ + ikvm_input.cpp \ + ikvm_manager.cpp \ + ikvm_server.cpp \ + ikvm_video.cpp \ + obmc-ikvm.cpp + +obmc_ikvm_CXXFLAGS = \ + $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \ + $(PHOSPHOR_LOGGING_CFLAGS) \ + $(PTHREAD_CFLAGS) \ + $(SDBUSPLUS_CFLAGS) \ + $(LIBVNCSERVER_CFLAGS) + +obmc_ikvm_LDFLAGS = \ + $(PHOSPHOR_DBUS_INTERFACES_LIBS) \ + $(PHOSPHOR_LOGGING_LIBS) \ + $(PTHREAD_LIBS) \ + $(SDBUSPLUS_LIBS) \ + $(LIBVNCSERVER_LIBS) diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/README.md b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/README.md new file mode 100644 index 000000000..70d6e1373 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/README.md @@ -0,0 +1,18 @@ +# OpenBMC IpKVM Server + +The obmc-ikvm application is a VNC server that provides access to the host +graphics output. The application interfaces with the video device on the BMC +that captures the host graphics, and then serves that video data on the RFB +(remote framebuffer, also known as VNC) protocol. The application also +interfaces with the BMC USB gadget device to pass HID events from the BMC to +the host, allowing the user to interact with the host system. + +## Usage + +Once the host is running and an appropriate HID gadget device is instantiated +on the BMC, the application can be started with the following command: +``` obmc-ikvm -v <video device path> -i <HID gadget device path> ``` + +For example: + +``` obmc-ikvm -v /dev/video0 -i /dev/hidg0 ``` diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/bootstrap.sh b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/bootstrap.sh new file mode 100644 index 000000000..50b75b7ee --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/bootstrap.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +AUTOCONF_FILES="Makefile.in aclocal.m4 ar-lib autom4te.cache compile \ + config.guess config.h.in config.sub configure depcomp install-sh \ + ltmain.sh missing *libtool test-driver" + +case $1 in + clean) + test -f Makefile && make maintainer-clean + for file in ${AUTOCONF_FILES}; do + find -name "$file" | xargs -r rm -rf + done + exit 0 + ;; +esac + +autoreconf -i +echo 'Run "./configure ${CONFIGURE_FLAGS} && make"' diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/configure.ac b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/configure.ac new file mode 100644 index 000000000..671316022 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/configure.ac @@ -0,0 +1,29 @@ +# Initialization +AC_PREREQ([2.69]) +AC_INIT([obmc-ikvm], [1.0], [https://github.com/openbmc/obmc-ikvm/issues]) +AC_LANG([C++]) +AC_CONFIG_HEADERS([config.h]) +AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz]) +AM_SILENT_RULES([yes]) + +# Checks for programs. +AC_PROG_CXX +AM_PROG_AR +AC_PROG_INSTALL +AC_PROG_MAKE_SET + +# Checks for typedefs, structures, and compiler characteristics. +AX_CXX_COMPILE_STDCXX_17([noext]) +AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS]) + +# Checks for libraries. +AC_CHECK_LIB([pthread], [pthread_create]) +PKG_CHECK_MODULES([LIBVNCSERVER], [libvncserver], , AC_MSG_ERROR(["Requires libvncserver package."])) +PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus], , AC_MSG_ERROR(["Requires sdbusplus package."])) +PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging], , AC_MSG_ERROR(["Requires phosphor-logging package."])) +PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces], , AC_MSG_ERROR(["Requires phosphor-dbus-interfaces package."])) + +LT_INIT + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/create_usbhid.sh b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/create_usbhid.sh new file mode 100644 index 000000000..955f18b61 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/create_usbhid.sh @@ -0,0 +1,135 @@ +#!/bin/sh + +new_directory="/sys/kernel/config/usb_gadget/obmc_input" + +if [ -e "${new_directory}" ]; then + exit 0 +fi + +# create gadget +original_directory="$(pwd)" +mkdir "${new_directory}" +cd "${new_directory}" + +# add basic information +echo 0x0100 > bcdDevice +echo 0x0200 > bcdUSB +echo 0x0104 > idProduct # Multifunction Composite Gadget +echo 0x1d6b > idVendor # Linux Foundation + +# create English locale +mkdir strings/0x409 + +echo "OpenBMC" > strings/0x409/manufacturer +echo "virtual_input" > strings/0x409/product +echo "OBMC0001" > strings/0x409/serialnumber + +# Create HID keyboard function +mkdir functions/hid.0 + +echo 1 > functions/hid.0/protocol # 1: keyboard +echo 8 > functions/hid.0/report_length +echo 1 > functions/hid.0/subclass + +# Binary HID keyboard descriptor +# 0x05, 0x01, // USAGE_PAGE (Generic Desktop) +# 0x09, 0x06, // USAGE (Keyboard) +# 0xa1, 0x01, // COLLECTION (Application) +# 0x05, 0x07, // USAGE_PAGE (Keyboard) +# 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) +# 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) +# 0x15, 0x00, // LOGICAL_MINIMUM (0) +# 0x25, 0x01, // LOGICAL_MAXIMUM (1) +# 0x75, 0x01, // REPORT_SIZE (1) +# 0x95, 0x08, // REPORT_COUNT (8) +# 0x81, 0x02, // INPUT (Data,Var,Abs) +# 0x95, 0x01, // REPORT_COUNT (1) +# 0x75, 0x08, // REPORT_SIZE (8) +# 0x81, 0x03, // INPUT (Data,Var,Abs) +# 0x95, 0x05, // REPORT_COUNT (5) +# 0x75, 0x01, // REPORT_SIZE (1) +# 0x05, 0x08, // USAGE_PAGE (LEDs) +# 0x19, 0x01, // USAGE_MINIMUM (Num Lock) +# 0x29, 0x05, // USAGE_MAXIMUM (Kana) +# 0x91, 0x02, // OUTPUT (Data,Var,Abs) +# 0x95, 0x01, // REPORT_COUNT (1) +# 0x75, 0x03, // REPORT_SIZE (3) +# 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) +# 0x95, 0x06, // REPORT_COUNT (6) +# 0x75, 0x08, // REPORT_SIZE (8) +# 0x15, 0x00, // LOGICAL_MINIMUM (0) +# 0x25, 0x65, // LOGICAL_MAXIMUM (101) +# 0x05, 0x07, // USAGE_PAGE (Keyboard) +# 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) +# 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) +# 0x81, 0x00, // INPUT (Data,Ary,Abs) +# 0xc0 // END_COLLECTION +echo -ne '\x05\x01\x09\x06\xa1\x01\x05\x07\x19\xe0\x29\xe7\x15\x00\x25\x01\x75\x01\x95\x08\x81\x02\x95\x01\x75\x08\x81\x03\x95\x05\x75\x01\x05\x08\x19\x01\x29\x05\x91\x02\x95\x01\x75\x03\x91\x03\x95\x06\x75\x08\x15\x00\x25\x65\x05\x07\x19\x00\x29\x65\x81\x00\xc0' > functions/hid.0/report_desc + +# Create HID mouse function +mkdir functions/hid.1 + +echo 2 > functions/hid.1/protocol # 2: mouse +echo 5 > functions/hid.1/report_length +echo 1 > functions/hid.1/subclass + +# Binary HID mouse descriptor (absolute coordinate) +# 0x05, 0x01, // USAGE_PAGE (Generic Desktop) +# 0x09, 0x02, // USAGE (Mouse) +# 0xa1, 0x01, // COLLECTION (Application) +# 0x09, 0x01, // USAGE (Pointer) +# 0xa1, 0x00, // COLLECTION (Physical) +# 0x05, 0x09, // USAGE_PAGE (Button) +# 0x19, 0x01, // USAGE_MINIMUM (Button 1) +# 0x29, 0x03, // USAGE_MAXIMUM (Button 3) +# 0x15, 0x00, // LOGICAL_MINIMUM (0) +# 0x25, 0x01, // LOGICAL_MAXIMUM (1) +# 0x95, 0x03, // REPORT_COUNT (3) +# 0x75, 0x01, // REPORT_SIZE (1) +# 0x81, 0x02, // INPUT (Data,Var,Abs) +# 0x95, 0x01, // REPORT_COUNT (1) +# 0x75, 0x05, // REPORT_SIZE (5) +# 0x81, 0x03, // INPUT (Cnst,Var,Abs) +# 0x05, 0x01, // USAGE_PAGE (Generic Desktop) +# 0x09, 0x30, // USAGE (X) +# 0x09, 0x31, // USAGE (Y) +# 0x35, 0x00, // PHYSICAL_MINIMUM (0) +# 0x46, 0xff, 0x7f, // PHYSICAL_MAXIMUM (32767) +# 0x15, 0x00, // LOGICAL_MINIMUM (0) +# 0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767) +# 0x65, 0x11, // UNIT (SI Lin:Distance) +# 0x55, 0x00, // UNIT_EXPONENT (0) +# 0x75, 0x10, // REPORT_SIZE (16) +# 0x95, 0x02, // REPORT_COUNT (2) +# 0x81, 0x02, // INPUT (Data,Var,Abs) +# 0xc0, // END_COLLECTION +# 0xc0 // END_COLLECTION +echo -ne '\x05\x01\x09\x02\xa1\x01\x09\x01\xa1\x00\x05\x09\x19\x01\x29\x03\x15\x00\x25\x01\x95\x03\x75\x01\x81\x02\x95\x01\x75\x05\x81\x03\x05\x01\x09\x30\x09\x31\x35\x00\x46\xff\x7f\x15\x00\x26\xff\x7f\x65\x11\x55\x00\x75\x10\x95\x02\x81\x02\xc0\xc0' > functions/hid.1/report_desc + +# Create configuration +mkdir configs/c.1 +mkdir configs/c.1/strings/0x409 + +echo 0x80 > configs/c.1/bmAttributes +echo 200 > configs/c.1/MaxPower +echo "" > configs/c.1/strings/0x409/configuration + +# Link HID functions to configuration +ln -s functions/hid.0 configs/c.1 +ln -s functions/hid.1 configs/c.1 + +# Enable gadget +dev_name="1e6a0000.usb-vhub" +i=0 +num_ports=5 +base_usb_dir="/sys/bus/platform/devices/${dev_name}/${dev_name}:p" +while [ $i -lt $num_ports ]; do + port=$(($i + 1)) + i=$port + if [ ! -e "${base_usb_dir}${port}/gadget/suspended" ]; then + break + fi +done +echo "${dev_name}:p${port}" > UDC + +cd "${original_directory}" diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_args.cpp b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_args.cpp new file mode 100644 index 000000000..2723187dd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_args.cpp @@ -0,0 +1,57 @@ +#include "ikvm_args.hpp" + +#include <getopt.h> +#include <rfb/rfb.h> +#include <stdio.h> +#include <stdlib.h> + +namespace ikvm +{ + +Args::Args(int argc, char* argv[]) : frameRate(30), commandLine(argc, argv) +{ + int option; + const char* opts = "f:h:k:p:v:"; + struct option lopts[] = {{"frameRate", 1, 0, 'f'}, {"help", 0, 0, 'h'}, + {"keyboard", 1, 0, 'k'}, {"mouse", 1, 0, 'p'}, + {"videoDevice", 1, 0, 'v'}, {0, 0, 0, 0}}; + + while ((option = getopt_long(argc, argv, opts, lopts, NULL)) != -1) + { + switch (option) + { + case 'f': + frameRate = (int)strtol(optarg, NULL, 0); + if (frameRate < 0 || frameRate > 60) + frameRate = 30; + break; + case 'h': + printUsage(); + exit(0); + case 'k': + keyboardPath = std::string(optarg); + break; + case 'p': + pointerPath = std::string(optarg); + break; + case 'v': + videoPath = std::string(optarg); + break; + } + } +} + +void Args::printUsage() +{ + // use fprintf(stderr to match rfbUsage() + fprintf(stderr, "OpenBMC IKVM daemon\n"); + fprintf(stderr, "Usage: obmc-ikvm [options]\n"); + fprintf(stderr, "-f frame rate try this frame rate\n"); + fprintf(stderr, "-h, --help show this message and exit\n"); + fprintf(stderr, "-k device HID keyboard gadget device\n"); + fprintf(stderr, "-p device HID mouse gadget device\n"); + fprintf(stderr, "-v device V4L2 device\n"); + rfbUsage(); +} + +} // namespace ikvm diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_args.hpp b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_args.hpp new file mode 100644 index 000000000..f877d32e9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_args.hpp @@ -0,0 +1,123 @@ +#pragma once + +#include <string> + +namespace ikvm +{ + +/* + * @class Args + * @brief Command line argument parser and storage + */ +class Args +{ + public: + /* + * @struct CommandLine + * @brief Stores the original command line arguments for later use + */ + struct CommandLine + { + /* + * @brief Constructs CommandLine object + * + * @param[in] c - Number of arguments + * @param[in] v - Array of arguments + */ + CommandLine(int c, char** v) : argc(c), argv(v) + { + } + ~CommandLine() = default; + CommandLine(const CommandLine&) = default; + CommandLine& operator=(const CommandLine&) = default; + CommandLine(CommandLine&&) = default; + CommandLine& operator=(CommandLine&&) = default; + + int argc; + char** argv; + }; + + /* + * @brief Constructs Args object + * + * @param[in] argc - The number of arguments in the command line call + * @param[in] argv - The array of arguments from the command line + */ + Args(int argc, char* argv[]); + ~Args() = default; + Args(const Args&) = default; + Args& operator=(const Args&) = default; + Args(Args&&) = default; + Args& operator=(Args&&) = default; + + /* + * @brief Get the original command line arguments + * + * @return Reference to the CommandLine structure storing the original + * command line arguments + */ + inline const CommandLine& getCommandLine() const + { + return commandLine; + } + + /* + * @brief Get the desired video frame rate + * + * @return Value of the desired frame rate in frames per second + */ + inline int getFrameRate() const + { + return frameRate; + } + + /* + * @brief Get the path to the USB keyboard device + * + * @return Reference to the string storing the path to the keyboard device + */ + inline const std::string& getKeyboardPath() const + { + return keyboardPath; + } + + /* + * @brief Get the path to the USB mouse device + * + * @return Reference to the string storing the path to the mouse device + */ + inline const std::string& getPointerPath() const + { + return pointerPath; + } + + /* + * @brief Get the path to the V4L2 video device + * + * @return Reference to the string storing the path to the video device + */ + inline const std::string& getVideoPath() const + { + return videoPath; + } + + private: + /* @brief Prints the application usage to stderr */ + void printUsage(); + + /* + * @brief Desired frame rate (in frames per second) of the video + * stream + */ + int frameRate; + /* @brief Path to the USB keyboard device */ + std::string keyboardPath; + /* @brief Path to the USB mouse device */ + std::string pointerPath; + /* @brief Path to the V4L2 video device */ + std::string videoPath; + /* @brief Original command line arguments passed to the application */ + CommandLine commandLine; +}; + +} // namespace ikvm diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_input.cpp b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_input.cpp new file mode 100644 index 000000000..f161f7327 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_input.cpp @@ -0,0 +1,380 @@ +#include "ikvm_input.hpp" + +#include "ikvm_server.hpp" +#include "scancodes.hpp" + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <rfb/keysym.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <phosphor-logging/elog-errors.hpp> +#include <phosphor-logging/elog.hpp> +#include <phosphor-logging/log.hpp> +#include <xyz/openbmc_project/Common/File/error.hpp> + +namespace ikvm +{ + +using namespace phosphor::logging; +using namespace sdbusplus::xyz::openbmc_project::Common::File::Error; + +Input::Input(const std::string& kbdPath, const std::string& ptrPath) : + keyboardFd(-1), pointerFd(-1), keyboardReport{0}, pointerReport{0}, + keyboardPath(kbdPath), pointerPath(ptrPath) +{ + if (!keyboardPath.empty()) + { + keyboardFd = open(keyboardPath.c_str(), O_RDWR | O_CLOEXEC); + if (keyboardFd < 0) + { + log<level::ERR>("Failed to open input device", + entry("PATH=%s", keyboardPath.c_str()), + entry("ERROR=%s", strerror(errno))); + elog<Open>(xyz::openbmc_project::Common::File::Open::ERRNO(errno), + xyz::openbmc_project::Common::File::Open::PATH( + keyboardPath.c_str())); + } + } + + if (!pointerPath.empty()) + { + pointerFd = open(pointerPath.c_str(), O_RDWR | O_CLOEXEC); + if (pointerFd < 0) + { + log<level::ERR>("Failed to open input device", + entry("PATH=%s", pointerPath.c_str()), + entry("ERROR=%s", strerror(errno))); + elog<Open>(xyz::openbmc_project::Common::File::Open::ERRNO(errno), + xyz::openbmc_project::Common::File::Open::PATH( + pointerPath.c_str())); + } + } +} + +Input::~Input() +{ + if (keyboardFd >= 0) + { + close(keyboardFd); + } + + if (pointerFd >= 0) + { + close(pointerFd); + } +} + +void Input::keyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl) +{ + Server::ClientData* cd = (Server::ClientData*)cl->clientData; + Input* input = cd->input; + + if (down) + { + uint8_t sc = keyToScancode(key); + + if (sc) + { + if (input->keysDown.find(key) == input->keysDown.end()) + { + for (unsigned int i = 2; i < KEY_REPORT_LENGTH; ++i) + { + if (!input->keyboardReport[i]) + { + input->keyboardReport[i] = sc; + input->keysDown.insert(std::make_pair(key, i)); + input->sendKeyboard = true; + break; + } + } + } + } + else + { + uint8_t mod = keyToMod(key); + + if (mod) + { + input->keyboardReport[0] |= mod; + input->sendKeyboard = true; + } + } + } + else + { + auto it = input->keysDown.find(key); + + if (it != input->keysDown.end()) + { + input->keyboardReport[it->second] = 0; + input->keysDown.erase(it); + input->sendKeyboard = true; + } + else + { + uint8_t mod = keyToMod(key); + + if (mod) + { + input->keyboardReport[0] &= ~mod; + input->sendKeyboard = true; + } + } + } +} + +void Input::pointerEvent(int buttonMask, int x, int y, rfbClientPtr cl) +{ + Server::ClientData* cd = (Server::ClientData*)cl->clientData; + Input* input = cd->input; + Server* server = (Server*)cl->screen->screenData; + const Video& video = server->getVideo(); + + input->pointerReport[0] = buttonMask & 0xFF; + + if (x >= 0 && (unsigned int)x < video.getWidth()) + { + uint16_t xx = x * ((SHRT_MAX + 1) / video.getWidth()); + + memcpy(&input->pointerReport[1], &xx, 2); + } + + if (y >= 0 && (unsigned int)y < video.getHeight()) + { + uint16_t yy = y * ((SHRT_MAX + 1) / video.getHeight()); + + memcpy(&input->pointerReport[3], &yy, 2); + } + + input->sendPointer = true; + rfbDefaultPtrAddEvent(buttonMask, x, y, cl); +} + +void Input::sendWakeupPacket() +{ + uint8_t wakeupReport[PTR_REPORT_LENGTH] = {0}; + uint16_t xy = SHRT_MAX / 2; + + if (pointerFd < 0) + { + return; + } + + memcpy(&wakeupReport[1], &xy, 2); + memcpy(&wakeupReport[3], &xy, 2); + + if (write(pointerFd, wakeupReport, PTR_REPORT_LENGTH) != PTR_REPORT_LENGTH) + { + log<level::ERR>("Failed to write report", + entry("ERROR=%s", strerror(errno))); + } +} + +void Input::sendReport() +{ + if (sendKeyboard && keyboardFd >= 0) + { + if (write(keyboardFd, keyboardReport, KEY_REPORT_LENGTH) != + KEY_REPORT_LENGTH) + { + log<level::ERR>("Failed to write keyboard report", + entry("ERROR=%s", strerror(errno))); + } + + sendKeyboard = false; + } + + if (sendPointer && pointerFd >= 0) + { + if (write(pointerFd, pointerReport, PTR_REPORT_LENGTH) != + PTR_REPORT_LENGTH) + { + log<level::ERR>("Failed to write pointer report", + entry("ERROR=%s", strerror(errno))); + } + + sendPointer = false; + } +} + +uint8_t Input::keyToMod(rfbKeySym key) +{ + uint8_t mod = 0; + + if (key >= XK_Shift_L && key <= XK_Control_R) + { + mod = shiftCtrlMap[key - XK_Shift_L]; + } + else if (key >= XK_Meta_L && key <= XK_Alt_R) + { + mod = metaAltMap[key - XK_Meta_L]; + } + + return mod; +} + +uint8_t Input::keyToScancode(rfbKeySym key) +{ + uint8_t scancode = 0; + + if ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z')) + { + scancode = USBHID_KEY_A + ((key & 0x5F) - 'A'); + } + else if (key >= '1' && key <= '9') + { + scancode = USBHID_KEY_1 + (key - '1'); + } + else if (key >= XK_F1 && key <= XK_F12) + { + scancode = USBHID_KEY_F1 + (key - XK_F1); + } + else + { + switch (key) + { + case XK_exclam: + scancode = USBHID_KEY_1; + break; + case XK_at: + scancode = USBHID_KEY_2; + break; + case XK_numbersign: + scancode = USBHID_KEY_3; + break; + case XK_dollar: + scancode = USBHID_KEY_4; + break; + case XK_percent: + scancode = USBHID_KEY_5; + break; + case XK_asciicircum: + scancode = USBHID_KEY_6; + break; + case XK_ampersand: + scancode = USBHID_KEY_7; + break; + case XK_asterisk: + scancode = USBHID_KEY_8; + break; + case XK_parenleft: + scancode = USBHID_KEY_9; + break; + case XK_0: + case XK_parenright: + scancode = USBHID_KEY_0; + break; + case XK_Return: + scancode = USBHID_KEY_RETURN; + break; + case XK_Escape: + scancode = USBHID_KEY_ESC; + break; + case XK_BackSpace: + scancode = USBHID_KEY_BACKSPACE; + break; + case XK_Tab: + scancode = USBHID_KEY_TAB; + break; + case XK_space: + scancode = USBHID_KEY_SPACE; + break; + case XK_minus: + case XK_underscore: + scancode = USBHID_KEY_MINUS; + break; + case XK_plus: + case XK_equal: + scancode = USBHID_KEY_EQUAL; + break; + case XK_bracketleft: + case XK_braceleft: + scancode = USBHID_KEY_LEFTBRACE; + break; + case XK_bracketright: + case XK_braceright: + scancode = USBHID_KEY_RIGHTBRACE; + break; + case XK_backslash: + case XK_bar: + scancode = USBHID_KEY_BACKSLASH; + break; + case XK_colon: + case XK_semicolon: + scancode = USBHID_KEY_SEMICOLON; + break; + case XK_quotedbl: + case XK_apostrophe: + scancode = USBHID_KEY_APOSTROPHE; + break; + case XK_grave: + case XK_asciitilde: + scancode = USBHID_KEY_GRAVE; + break; + case XK_comma: + case XK_less: + scancode = USBHID_KEY_COMMA; + break; + case XK_period: + case XK_greater: + scancode = USBHID_KEY_DOT; + break; + case XK_slash: + case XK_question: + scancode = USBHID_KEY_SLASH; + break; + case XK_Caps_Lock: + scancode = USBHID_KEY_CAPSLOCK; + break; + case XK_Print: + scancode = USBHID_KEY_PRINT; + break; + case XK_Scroll_Lock: + scancode = USBHID_KEY_SCROLLLOCK; + break; + case XK_Pause: + scancode = USBHID_KEY_PAUSE; + break; + case XK_Insert: + scancode = USBHID_KEY_INSERT; + break; + case XK_Home: + scancode = USBHID_KEY_HOME; + break; + case XK_Page_Up: + scancode = USBHID_KEY_PAGEUP; + break; + case XK_Delete: + scancode = USBHID_KEY_DELETE; + break; + case XK_End: + scancode = USBHID_KEY_END; + break; + case XK_Page_Down: + scancode = USBHID_KEY_PAGEDOWN; + break; + case XK_Right: + scancode = USBHID_KEY_RIGHT; + break; + case XK_Left: + scancode = USBHID_KEY_LEFT; + break; + case XK_Down: + scancode = USBHID_KEY_DOWN; + break; + case XK_Up: + scancode = USBHID_KEY_UP; + break; + case XK_Num_Lock: + scancode = USBHID_KEY_NUMLOCK; + break; + } + } + + return scancode; +} + +} // namespace ikvm diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_input.hpp b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_input.hpp new file mode 100644 index 000000000..f7413a4cd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_input.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include <rfb/rfb.h> + +#include <map> +#include <string> + +namespace ikvm +{ + +/* + * @class Input + * @brief Receives events from RFB clients and sends reports to the USB input + * device + */ +class Input +{ + public: + /* + * @brief Constructs Input object + * + * @param[in] kbdPath - Path to the USB keyboard device + * @param[in] ptrPath - Path to the USB mouse device + */ + Input(const std::string& kbdPath, const std::string& ptrPath); + ~Input(); + Input(const Input&) = default; + Input& operator=(const Input&) = default; + Input(Input&&) = default; + Input& operator=(Input&&) = default; + + /* + * @brief RFB client key event handler + * + * @param[in] down - Boolean indicating whether key is pressed or not + * @param[in] key - Key code + * @param[in] cl - Handle to the RFB client + */ + static void keyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl); + /* + * @brief RFB client pointer event handler + * + * @param[in] buttonMask - Bitmask indicating which buttons have been + * pressed + * @param[in] x - Pointer x-coordinate + * @param[in] y - Pointer y-coordinate + * @param[in] cl - Handle to the RFB client + */ + static void pointerEvent(int buttonMask, int x, int y, rfbClientPtr cl); + + /* @brief Sends a wakeup data packet to the USB input device */ + void sendWakeupPacket(); + /* @brief Sends an HID report to the USB input device */ + void sendReport(); + + private: + static constexpr int NUM_MODIFIER_BITS = 4; + static constexpr int KEY_REPORT_LENGTH = 8; + static constexpr int PTR_REPORT_LENGTH = 5; + + /* @brief HID modifier bits mapped to shift and control key codes */ + static constexpr uint8_t shiftCtrlMap[NUM_MODIFIER_BITS] = { + 0x02, // left shift + 0x20, // right shift + 0x01, // left control + 0x10 // right control + }; + /* @brief HID modifier bits mapped to meta and alt key codes */ + static constexpr uint8_t metaAltMap[NUM_MODIFIER_BITS] = { + 0x08, // left meta + 0x80, // right meta + 0x04, // left alt + 0x40 // right alt + }; + /* + * @brief Translates a RFB-specific key code to HID modifier bit + * + * @param[in] key - key code + */ + static uint8_t keyToMod(rfbKeySym key); + /* + * @brief Translates a RFB-specific key code to HID scancode + * + * @param[in] key - key code + */ + static uint8_t keyToScancode(rfbKeySym key); + + /* @brief Indicates whether or not to send a keyboard report */ + bool sendKeyboard; + /* @brief Indicates whether or not to send a pointer report */ + bool sendPointer; + /* @brief File descriptor for the USB keyboard device */ + int keyboardFd; + /* @brief File descriptor for the USB mouse device */ + int pointerFd; + /* @brief Data for keyboard report */ + uint8_t keyboardReport[KEY_REPORT_LENGTH]; + /* @brief Data for pointer report */ + uint8_t pointerReport[PTR_REPORT_LENGTH]; + /* @brief Path to the USB keyboard device */ + std::string keyboardPath; + /* @brief Path to the USB mouse device */ + std::string pointerPath; + /* + * @brief Mapping of RFB key code to report data index to keep track + * of which keys are down + */ + std::map<int, int> keysDown; +}; + +} // namespace ikvm diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_manager.cpp b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_manager.cpp new file mode 100644 index 000000000..5e014d057 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_manager.cpp @@ -0,0 +1,100 @@ +#include "ikvm_manager.hpp" + +#include <thread> + +namespace ikvm +{ + +Manager::Manager(const Args& args) : + continueExecuting(true), serverDone(false), videoDone(true), + input(args.getKeyboardPath(), args.getPointerPath()), + video(args.getVideoPath(), input, args.getFrameRate()), + server(args, input, video) +{ +} + +void Manager::run() +{ + std::thread run(serverThread, this); + + while (continueExecuting) + { + if (server.wantsFrame()) + { + video.getFrame(); + server.sendFrame(); + } + else + { + video.stop(); + } + + if (video.needsResize()) + { + videoDone = false; + waitServer(); + video.resize(); + server.resize(); + setVideoDone(); + } + else + { + setVideoDone(); + waitServer(); + } + } + + run.join(); +} + +void Manager::serverThread(Manager* manager) +{ + while (manager->continueExecuting) + { + manager->server.run(); + manager->setServerDone(); + manager->waitVideo(); + } +} + +void Manager::setServerDone() +{ + std::unique_lock<std::mutex> ulock(lock); + + serverDone = true; + sync.notify_all(); +} + +void Manager::setVideoDone() +{ + std::unique_lock<std::mutex> ulock(lock); + + videoDone = true; + sync.notify_all(); +} + +void Manager::waitServer() +{ + std::unique_lock<std::mutex> ulock(lock); + + while (!serverDone) + { + sync.wait(ulock); + } + + serverDone = false; +} + +void Manager::waitVideo() +{ + std::unique_lock<std::mutex> ulock(lock); + + while (!videoDone) + { + sync.wait(ulock); + } + + // don't reset videoDone +} + +} // namespace ikvm diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_manager.hpp b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_manager.hpp new file mode 100644 index 000000000..67d5a681e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_manager.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include "ikvm_args.hpp" +#include "ikvm_input.hpp" +#include "ikvm_server.hpp" +#include "ikvm_video.hpp" + +#include <condition_variable> +#include <mutex> + +namespace ikvm +{ + +/* + * @class Manager + * @brief Manages the VNC server by executing threaded loops of RFB operations + * and video device operations. + */ +class Manager +{ + public: + /* + * @brief Constructs the Manager object + * + * @param[in] args - Reference to Args object + */ + Manager(const Args& args); + ~Manager() = default; + Manager(const Manager&) = default; + Manager& operator=(const Manager&) = default; + Manager(Manager&&) = default; + Manager& operator=(Manager&&) = default; + + /* @brief Begins operation of the VNC server */ + void run(); + + private: + /* + * @brief Thread function to loop the RFB update operations + * + * @param[in] manager - Pointer to the Manager object + */ + static void serverThread(Manager* manager); + + /* @brief Notifies thread waiters that RFB operations are complete */ + void setServerDone(); + /* @brief Notifies thread waiters that video operations are complete */ + void setVideoDone(); + /* @brief Blocks until RFB operations complete */ + void waitServer(); + /* @brief Blocks until video operations are complete */ + void waitVideo(); + + /* + * @brief Boolean to indicate whether the application should continue + * running + */ + bool continueExecuting; + /* @brief Boolean to indicate that RFB operations are complete */ + bool serverDone; + /* @brief Boolean to indicate that video operations are complete */ + bool videoDone; + /* @brief Input object */ + Input input; + /* @brief Video object */ + Video video; + /* @brief RFB server object */ + Server server; + /* @brief Condition variable to enable waiting for thread completion */ + std::condition_variable sync; + /* @brief Mutex for waiting on condition variable safely */ + std::mutex lock; +}; + +} // namespace ikvm diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_server.cpp b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_server.cpp new file mode 100644 index 000000000..47737587e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_server.cpp @@ -0,0 +1,218 @@ +#include "ikvm_server.hpp" + +#include <rfb/rfbproto.h> + +#include <phosphor-logging/elog-errors.hpp> +#include <phosphor-logging/elog.hpp> +#include <phosphor-logging/log.hpp> +#include <xyz/openbmc_project/Common/error.hpp> + +namespace ikvm +{ + +using namespace phosphor::logging; +using namespace sdbusplus::xyz::openbmc_project::Common::Error; + +Server::Server(const Args& args, Input& i, Video& v) : + pendingResize(false), frameCounter(0), numClients(0), input(i), video(v) +{ + std::string ip("localhost"); + const Args::CommandLine& commandLine = args.getCommandLine(); + int argc = commandLine.argc; + + server = rfbGetScreen(&argc, commandLine.argv, video.getWidth(), + video.getHeight(), Video::bitsPerSample, + Video::samplesPerPixel, Video::bytesPerPixel); + + if (!server) + { + log<level::ERR>("Failed to get VNC screen due to invalid arguments"); + elog<InvalidArgument>( + xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_NAME(""), + xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_VALUE("")); + } + + framebuffer.resize( + video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0); + + server->screenData = this; + server->desktopName = "OpenBMC IKVM"; + server->alwaysShared = true; + server->frameBuffer = framebuffer.data(); + server->newClientHook = newClient; + server->cursor = rfbMakeXCursor(cursorWidth, cursorHeight, (char*)cursor, + (char*)cursorMask); + server->cursor->xhot = 1; + server->cursor->yhot = 1; + // char httpDir[] = "../webclients"; + // server->httpDir = httpDir; + // server->httpEnableProxyConnect = true; + + // commented it out to allow OOB connection + // rfbStringToAddr(&ip[0], &server->listenInterface); + + rfbInitServer(server); + + rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight()); + + server->kbdAddEvent = Input::keyEvent; + server->ptrAddEvent = Input::pointerEvent; + + processTime = (1000000 / video.getFrameRate()) - 100; +} + +Server::~Server() +{ + rfbScreenCleanup(server); +} + +void Server::resize() +{ + if (frameCounter > video.getFrameRate()) + { + doResize(); + } + else + { + pendingResize = true; + } +} + +void Server::run() +{ + rfbProcessEvents(server, processTime); + + if (server->clientHead) + { + input.sendReport(); + + frameCounter++; + if (pendingResize && frameCounter > video.getFrameRate()) + { + doResize(); + pendingResize = false; + } + } +} + +void Server::sendFrame() +{ + char* data = video.getData(); + rfbClientIteratorPtr it; + rfbClientPtr cl; + + if (!data || pendingResize) + { + return; + } + + it = rfbGetClientIterator(server); + + while ((cl = rfbClientIteratorNext(it))) + { + ClientData* cd = (ClientData*)cl->clientData; + rfbFramebufferUpdateMsg* fu = (rfbFramebufferUpdateMsg*)cl->updateBuf; + + if (!cd) + { + continue; + } + + if (cd->skipFrame) + { + cd->skipFrame--; + continue; + } + + if (cl->enableLastRectEncoding) + { + fu->nRects = 0xFFFF; + } + else + { + fu->nRects = Swap16IfLE(1); + } + + fu->type = rfbFramebufferUpdate; + cl->ublen = sz_rfbFramebufferUpdateMsg; + rfbSendUpdateBuf(cl); + + cl->tightEncoding = rfbEncodingTight; + rfbSendTightHeader(cl, 0, 0, video.getWidth(), video.getHeight()); + + cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4); + rfbSendCompressedDataTight(cl, data, video.getFrameSize()); + + if (cl->enableLastRectEncoding) + { + rfbSendLastRectMarker(cl); + } + + rfbSendUpdateBuf(cl); + } + + rfbReleaseClientIterator(it); +} + +void Server::clientGone(rfbClientPtr cl) +{ + Server* server = (Server*)cl->screen->screenData; + + delete (ClientData*)cl->clientData; + + if (server->numClients-- == 1) + { + rfbMarkRectAsModified(server->server, 0, 0, server->video.getWidth(), + server->video.getHeight()); + } +} + +enum rfbNewClientAction Server::newClient(rfbClientPtr cl) +{ + Server* server = (Server*)cl->screen->screenData; + + cl->clientData = + new ClientData(server->video.getFrameRate(), &server->input); + cl->clientGoneHook = clientGone; + if (!server->numClients++) + { + server->pendingResize = false; + server->frameCounter = 0; + server->video.start(); + } + + return RFB_CLIENT_ACCEPT; +} + +void Server::doResize() +{ + rfbClientIteratorPtr it; + rfbClientPtr cl; + + framebuffer.resize( + video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0); + + rfbNewFramebuffer(server, framebuffer.data(), video.getWidth(), + video.getHeight(), Video::bitsPerSample, + Video::samplesPerPixel, Video::bytesPerPixel); + rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight()); + + it = rfbGetClientIterator(server); + + while ((cl = rfbClientIteratorNext(it))) + { + ClientData* cd = (ClientData*)cl->clientData; + + if (!cd) + { + continue; + } + + // delay video updates to give the client time to resize + cd->skipFrame = video.getFrameRate(); + } + + rfbReleaseClientIterator(it); +} + +} // namespace ikvm diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_server.hpp b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_server.hpp new file mode 100644 index 000000000..b8062017b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_server.hpp @@ -0,0 +1,167 @@ +#pragma once + +#include "ikvm_args.hpp" +#include "ikvm_input.hpp" +#include "ikvm_video.hpp" + +#include <rfb/rfb.h> + +#include <vector> + +namespace ikvm +{ + +/* + * @class Server + * @brief Manages the RFB server connection and updates + */ +class Server +{ + public: + /* + * @struct ClientData + * @brief Store necessary data for each connected RFB client + */ + struct ClientData + { + /* + * @brief Constructs ClientData object + * + * @param[in] s - Number of frames to skip when client connects + * @param[in] i - Pointer to Input object + */ + ClientData(int s, Input* i) : skipFrame(s), input(i) + { + } + ~ClientData() = default; + ClientData(const ClientData&) = default; + ClientData& operator=(const ClientData&) = default; + ClientData(ClientData&&) = default; + ClientData& operator=(ClientData&&) = default; + + int skipFrame; + Input* input; + }; + + /* + * @brief Constructs Server object + * + * @param[in] args - Reference to Args object + * @param[in] i - Reference to Input object + * @param[in] v - Reference to Video object + */ + Server(const Args& args, Input& i, Video& v); + ~Server(); + Server(const Server&) = default; + Server& operator=(const Server&) = default; + Server(Server&&) = default; + Server& operator=(Server&&) = default; + + /* @brief Resizes the RFB framebuffer */ + void resize(); + /* @brief Executes any pending RFB updates and client input */ + void run(); + /* @brief Sends pending video frame to clients */ + void sendFrame(); + + /* + * @brief Indicates whether or not video data is desired + * + * @return Boolean to indicate whether any clients need a video frame + */ + inline bool wantsFrame() const + { + return server->clientHead; + } + /* + * @brief Get the Video object + * + * @return Reference to the Video object + */ + inline const Video& getVideo() const + { + return video; + } + + private: + /* + * @brief Handler for a client disconnecting + * + * @param[in] cl - Handle to the client object + */ + static void clientGone(rfbClientPtr cl); + /* + * @brief Handler for client connecting + * + * @param[in] cl - Handle to the client object + */ + static enum rfbNewClientAction newClient(rfbClientPtr cl); + + /* @brief Performs the resize operation on the framebuffer */ + void doResize(); + + /* @brief Boolean to indicate if a resize operation is on-going */ + bool pendingResize; + /* @brief Number of frames handled since a client connected */ + int frameCounter; + /* @brief Number of connected clients */ + unsigned int numClients; + /* @brief Microseconds to process RFB events every frame */ + long int processTime; + /* @brief Handle to the RFB server object */ + rfbScreenInfoPtr server; + /* @brief Reference to the Input object */ + Input& input; + /* @brief Reference to the Video object */ + Video& video; + /* @brief Default framebuffer storage */ + std::vector<char> framebuffer; + /* @brief Cursor bitmap width */ + static constexpr int cursorWidth = 20; + /* @brief Cursor bitmap height */ + static constexpr int cursorHeight = 20; + /* @brief Cursor bitmap */ + static constexpr char cursor[] = " " + " x " + " xx " + " xxx " + " xxxx " + " xxxxx " + " xxxxxx " + " xxxxxxx " + " xxxxxxxx " + " xxxxxxxxx " + " xxxxxxxxxx " + " xxxxxxxxxxx " + " xxxxxxx " + " xxxxxxx " + " xxx xxx " + " xx xxx " + " x xxx " + " xxx " + " x " + " "; + /* @brief Cursor bitmap mask */ + static constexpr char cursorMask[] = " o " + "oxo " + "oxxo " + "oxxxo " + "oxxxxo " + "oxxxxxo " + "oxxxxxxo " + "oxxxxxxxo " + "oxxxxxxxxo " + "oxxxxxxxxxo " + "oxxxxxxxxxxo " + "oxxxxxxxxxxxo " + "oxxxxxxxoooo " + "oxxxxxxxo " + "oxxxooxxxo " + "oxxo oxxxo " + "oxo oxxxo " + " o oxxxo " + " oxo " + " o "; +}; + +} // namespace ikvm diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_video.cpp b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_video.cpp new file mode 100644 index 000000000..13de54da1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_video.cpp @@ -0,0 +1,478 @@ +#include "ikvm_video.hpp" + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/videodev2.h> +#include <poll.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <phosphor-logging/elog-errors.hpp> +#include <phosphor-logging/elog.hpp> +#include <phosphor-logging/log.hpp> +#include <xyz/openbmc_project/Common/Device/error.hpp> +#include <xyz/openbmc_project/Common/File/error.hpp> + +namespace ikvm +{ + +const int Video::bitsPerSample(8); +const int Video::bytesPerPixel(4); +const int Video::samplesPerPixel(3); + +using namespace phosphor::logging; +using namespace sdbusplus::xyz::openbmc_project::Common::File::Error; +using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error; + +Video::Video(const std::string& p, Input& input, int fr) : + resizeAfterOpen(false), fd(-1), frameRate(fr), lastFrameIndex(-1), + height(600), width(800), input(input), path(p) +{ +} + +Video::~Video() +{ + stop(); +} + +char* Video::getData() +{ + if (lastFrameIndex >= 0) + { + return (char*)buffers[lastFrameIndex].data; + } + + return nullptr; +} + +void Video::getFrame() +{ + bool queue(false); + int rc(0); + v4l2_buffer buf; + + if (fd < 0) + { + return; + } + + memset(&buf, 0, sizeof(v4l2_buffer)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + + while (rc >= 0) + { + rc = ioctl(fd, VIDIOC_DQBUF, &buf); + if (rc >= 0) + { + buffers[buf.index].queued = false; + + if (!(buf.flags & V4L2_BUF_FLAG_ERROR)) + { + lastFrameIndex = buf.index; + buffers[lastFrameIndex].payload = buf.bytesused; + queue = true; + break; + } + else + { + buffers[buf.index].payload = 0; + } + } + else + { + restart(); + return; + } + } + + if (queue) + { + for (unsigned int i = 0; i < buffers.size(); ++i) + { + if (i == (unsigned int)lastFrameIndex) + { + continue; + } + + if (!buffers[i].queued) + { + memset(&buf, 0, sizeof(v4l2_buffer)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + rc = ioctl(fd, VIDIOC_QBUF, &buf); + if (rc) + { + log<level::ERR>("Failed to queue buffer", + entry("ERROR=%s", strerror(errno))); + elog<ReadFailure>( + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_ERRNO(errno), + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_DEVICE_PATH(path.c_str())); + } + + buffers[i].queued = true; + } + } + } +} + +bool Video::needsResize() +{ + int rc; + v4l2_dv_timings timings; + + if (fd < 0) + { + return false; + } + + if (resizeAfterOpen) + { + return true; + } + + memset(&timings, 0, sizeof(v4l2_dv_timings)); + rc = ioctl(fd, VIDIOC_QUERY_DV_TIMINGS, &timings); + if (rc < 0) + { + log<level::ERR>("Failed to query timings", + entry("ERROR=%s", strerror(errno))); + return false; + } + + if (timings.bt.width != width || timings.bt.height != height) + { + width = timings.bt.width; + height = timings.bt.height; + + if (!width || !height) + { + log<level::ERR>("Failed to get new resolution", + entry("WIDTH=%d", width), + entry("HEIGHT=%d", height)); + elog<Open>( + xyz::openbmc_project::Common::File::Open::ERRNO(-EPROTO), + xyz::openbmc_project::Common::File::Open::PATH(path.c_str())); + } + + lastFrameIndex = -1; + return true; + } + + return false; +} + +void Video::resize() +{ + int rc; + unsigned int i; + bool needsResizeCall(false); + v4l2_buf_type type(V4L2_BUF_TYPE_VIDEO_CAPTURE); + v4l2_requestbuffers req; + + if (fd < 0) + { + return; + } + + if (resizeAfterOpen) + { + resizeAfterOpen = false; + return; + } + + for (i = 0; i < buffers.size(); ++i) + { + if (buffers[i].data) + { + needsResizeCall = true; + break; + } + } + + if (needsResizeCall) + { + rc = ioctl(fd, VIDIOC_STREAMOFF, &type); + if (rc) + { + log<level::ERR>("Failed to stop streaming", + entry("ERROR=%s", strerror(errno))); + elog<ReadFailure>( + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_ERRNO(errno), + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_DEVICE_PATH(path.c_str())); + } + } + + for (i = 0; i < buffers.size(); ++i) + { + if (buffers[i].data) + { + munmap(buffers[i].data, buffers[i].size); + buffers[i].data = nullptr; + buffers[i].queued = false; + } + } + + if (needsResizeCall) + { + v4l2_dv_timings timings; + + memset(&req, 0, sizeof(v4l2_requestbuffers)); + req.count = 0; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + rc = ioctl(fd, VIDIOC_REQBUFS, &req); + if (rc < 0) + { + log<level::ERR>("Failed to zero streaming buffers", + entry("ERROR=%s", strerror(errno))); + elog<ReadFailure>( + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_ERRNO(errno), + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_DEVICE_PATH(path.c_str())); + } + + memset(&timings, 0, sizeof(v4l2_dv_timings)); + rc = ioctl(fd, VIDIOC_QUERY_DV_TIMINGS, &timings); + if (rc < 0) + { + log<level::ERR>("Failed to query timings", + entry("ERROR=%s", strerror(errno))); + elog<ReadFailure>( + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_ERRNO(errno), + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_DEVICE_PATH(path.c_str())); + } + + rc = ioctl(fd, VIDIOC_S_DV_TIMINGS, &timings); + if (rc < 0) + { + log<level::ERR>("Failed to set timings", + entry("ERROR=%s", strerror(errno))); + elog<ReadFailure>( + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_ERRNO(errno), + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_DEVICE_PATH(path.c_str())); + } + + buffers.clear(); + } + + memset(&req, 0, sizeof(v4l2_requestbuffers)); + req.count = 3; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + rc = ioctl(fd, VIDIOC_REQBUFS, &req); + if (rc < 0 || req.count < 2) + { + log<level::ERR>("Failed to request streaming buffers", + entry("ERROR=%s", strerror(errno))); + elog<ReadFailure>( + xyz::openbmc_project::Common::Device::ReadFailure::CALLOUT_ERRNO( + errno), + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_DEVICE_PATH(path.c_str())); + } + + buffers.resize(req.count); + + for (i = 0; i < buffers.size(); ++i) + { + v4l2_buffer buf; + + memset(&buf, 0, sizeof(v4l2_buffer)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + rc = ioctl(fd, VIDIOC_QUERYBUF, &buf); + if (rc < 0) + { + log<level::ERR>("Failed to query buffer", + entry("ERROR=%s", strerror(errno))); + elog<ReadFailure>( + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_ERRNO(errno), + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_DEVICE_PATH(path.c_str())); + } + + buffers[i].data = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, buf.m.offset); + if (buffers[i].data == MAP_FAILED) + { + log<level::ERR>("Failed to mmap buffer", + entry("ERROR=%s", strerror(errno))); + elog<ReadFailure>( + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_ERRNO(errno), + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_DEVICE_PATH(path.c_str())); + } + + buffers[i].size = buf.length; + + rc = ioctl(fd, VIDIOC_QBUF, &buf); + if (rc < 0) + { + log<level::ERR>("Failed to queue buffer", + entry("ERROR=%s", strerror(errno))); + elog<ReadFailure>( + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_ERRNO(errno), + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_DEVICE_PATH(path.c_str())); + } + + buffers[i].queued = true; + } + + rc = ioctl(fd, VIDIOC_STREAMON, &type); + if (rc) + { + log<level::ERR>("Failed to start streaming", + entry("ERROR=%s", strerror(errno))); + elog<ReadFailure>( + xyz::openbmc_project::Common::Device::ReadFailure::CALLOUT_ERRNO( + errno), + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_DEVICE_PATH(path.c_str())); + } +} + +void Video::start() +{ + int rc; + size_t oldHeight = height; + size_t oldWidth = width; + v4l2_capability cap; + v4l2_format fmt; + v4l2_streamparm sparm; + + if (fd >= 0) + { + return; + } + + fd = open(path.c_str(), O_RDWR); + if (fd < 0) + { + input.sendWakeupPacket(); + + fd = open(path.c_str(), O_RDWR); + if (fd < 0) + { + log<level::ERR>("Failed to open video device", + entry("PATH=%s", path.c_str()), + entry("ERROR=%s", strerror(errno))); + elog<Open>( + xyz::openbmc_project::Common::File::Open::ERRNO(errno), + xyz::openbmc_project::Common::File::Open::PATH(path.c_str())); + } + } + + memset(&cap, 0, sizeof(v4l2_capability)); + rc = ioctl(fd, VIDIOC_QUERYCAP, &cap); + if (rc < 0) + { + log<level::ERR>("Failed to query video device capabilities", + entry("ERROR=%s", strerror(errno))); + elog<ReadFailure>( + xyz::openbmc_project::Common::Device::ReadFailure::CALLOUT_ERRNO( + errno), + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_DEVICE_PATH(path.c_str())); + } + + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) || + !(cap.capabilities & V4L2_CAP_STREAMING)) + { + log<level::ERR>("Video device doesn't support this application"); + elog<Open>( + xyz::openbmc_project::Common::File::Open::ERRNO(errno), + xyz::openbmc_project::Common::File::Open::PATH(path.c_str())); + } + + memset(&fmt, 0, sizeof(v4l2_format)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rc = ioctl(fd, VIDIOC_G_FMT, &fmt); + if (rc < 0) + { + log<level::ERR>("Failed to query video device format", + entry("ERROR=%s", strerror(errno))); + elog<ReadFailure>( + xyz::openbmc_project::Common::Device::ReadFailure::CALLOUT_ERRNO( + errno), + xyz::openbmc_project::Common::Device::ReadFailure:: + CALLOUT_DEVICE_PATH(path.c_str())); + } + + memset(&sparm, 0, sizeof(v4l2_streamparm)); + sparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + sparm.parm.capture.timeperframe.numerator = 1; + sparm.parm.capture.timeperframe.denominator = frameRate; + rc = ioctl(fd, VIDIOC_S_PARM, &sparm); + if (rc < 0) + { + log<level::WARNING>("Failed to set video device frame rate", + entry("ERROR=%s", strerror(errno))); + } + + height = fmt.fmt.pix.height; + width = fmt.fmt.pix.width; + + resize(); + + if (oldHeight != height || oldWidth != width) + { + resizeAfterOpen = true; + } +} + +void Video::stop() +{ + int rc; + unsigned int i; + v4l2_buf_type type(V4L2_BUF_TYPE_VIDEO_CAPTURE); + + if (fd < 0) + { + return; + } + + lastFrameIndex = -1; + + rc = ioctl(fd, VIDIOC_STREAMOFF, &type); + if (rc) + { + log<level::ERR>("Failed to stop streaming", + entry("ERROR=%s", strerror(errno))); + } + + for (i = 0; i < buffers.size(); ++i) + { + if (buffers[i].data) + { + munmap(buffers[i].data, buffers[i].size); + buffers[i].data = nullptr; + buffers[i].queued = false; + } + } + + close(fd); + fd = -1; +} + +} // namespace ikvm diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_video.hpp b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_video.hpp new file mode 100644 index 000000000..8ce5319f5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/ikvm_video.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include "ikvm_input.hpp" + +#include <mutex> +#include <string> +#include <vector> + +namespace ikvm +{ + +/* + * @class Video + * @brief Sets up the V4L2 video device and performs read operations + */ +class Video +{ + public: + /* + * @brief Constructs Video object + * + * @param[in] p - Path to the V4L2 video device + * @param[in] input - Reference to the Input object + * @param[in] fr - desired frame rate of the video + */ + Video(const std::string& p, Input& input, int fr = 30); + ~Video(); + Video(const Video&) = default; + Video& operator=(const Video&) = default; + Video(Video&&) = default; + Video& operator=(Video&&) = default; + + /* + * @brief Gets the video frame data + * + * @return Pointer to the video frame data + */ + char* getData(); + /* @brief Performs read to grab latest video frame */ + void getFrame(); + /* + * @brief Gets whether or not the video frame needs to be resized + * + * @return Boolean indicating if the frame needs to be resized + */ + bool needsResize(); + /* @brief Performs the resize and re-allocates framebuffer */ + void resize(); + /* @brief Starts streaming from the video device */ + void start(); + /* @brief Stops streaming from the video device */ + void stop(); + + /* @brief Restart streaming from the video device */ + inline void restart() + { + stop(); + start(); + } + /* + * @brief Gets the desired video frame rate in frames per second + * + * @return Value of the desired frame rate + */ + inline int getFrameRate() const + { + return frameRate; + } + /* + * @brief Gets the size of the video frame data + * + * @return Value of the size of the video frame data in bytes + */ + inline size_t getFrameSize() const + { + return buffers[lastFrameIndex].payload; + } + /* + * @brief Gets the height of the video frame + * + * @return Value of the height of video frame in pixels + */ + inline size_t getHeight() const + { + return height; + } + /* + * @brief Gets the width of the video frame + * + * @return Value of the width of video frame in pixels + */ + inline size_t getWidth() const + { + return width; + } + + /* @brief Number of bits per component of a pixel */ + static const int bitsPerSample; + /* @brief Number of bytes of storage for a pixel */ + static const int bytesPerPixel; + /* @brief Number of components in a pixel (i.e. 3 for RGB pixel) */ + static const int samplesPerPixel; + + private: + /* + * @struct Buffer + * @brief Store the address and size of frame data from streaming + * operations + */ + struct Buffer + { + Buffer() : data(nullptr), queued(false), payload(0), size(0) + { + } + ~Buffer() = default; + Buffer(const Buffer&) = default; + Buffer& operator=(const Buffer&) = default; + Buffer(Buffer&&) = default; + Buffer& operator=(Buffer&&) = default; + + void* data; + bool queued; + size_t payload; + size_t size; + }; + + /* + * @brief Boolean to indicate whether the resize was triggered during + * the open operation + */ + bool resizeAfterOpen; + /* @brief File descriptor for the V4L2 video device */ + int fd; + /* @brief Desired frame rate of video stream in frames per second */ + int frameRate; + /* @brief Buffer index for the last video frame */ + int lastFrameIndex; + /* @brief Height in pixels of the video frame */ + size_t height; + /* @brief Width in pixels of the video frame */ + size_t width; + /* @brief Reference to the Input object */ + Input& input; + /* @brief Path to the V4L2 video device */ + const std::string path; + /* @brief Streaming buffer storage */ + std::vector<Buffer> buffers; +}; + +} // namespace ikvm diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/obmc-ikvm.cpp b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/obmc-ikvm.cpp new file mode 100644 index 000000000..271857b72 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/obmc-ikvm.cpp @@ -0,0 +1,12 @@ +#include "ikvm_args.hpp" +#include "ikvm_manager.hpp" + +int main(int argc, char* argv[]) +{ + ikvm::Args args(argc, argv); + ikvm::Manager manager(args); + + manager.run(); + + return 0; +} diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/scancodes.hpp b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/scancodes.hpp new file mode 100644 index 000000000..db79231a2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/scancodes.hpp @@ -0,0 +1,82 @@ +#pragma once + +#define USBHID_KEY_A 0x04 +#define USBHID_KEY_B 0x05 +#define USBHID_KEY_C 0x06 +#define USBHID_KEY_D 0x07 +#define USBHID_KEY_E 0x08 +#define USBHID_KEY_F 0x09 +#define USBHID_KEY_G 0x0a +#define USBHID_KEY_H 0x0b +#define USBHID_KEY_I 0x0c +#define USBHID_KEY_J 0x0d +#define USBHID_KEY_K 0x0e +#define USBHID_KEY_L 0x0f +#define USBHID_KEY_M 0x10 +#define USBHID_KEY_N 0x11 +#define USBHID_KEY_O 0x12 +#define USBHID_KEY_P 0x13 +#define USBHID_KEY_Q 0x14 +#define USBHID_KEY_R 0x15 +#define USBHID_KEY_S 0x16 +#define USBHID_KEY_T 0x17 +#define USBHID_KEY_U 0x18 +#define USBHID_KEY_V 0x19 +#define USBHID_KEY_W 0x1a +#define USBHID_KEY_X 0x1b +#define USBHID_KEY_Y 0x1c +#define USBHID_KEY_Z 0x1d +#define USBHID_KEY_1 0x1e +#define USBHID_KEY_2 0x1f +#define USBHID_KEY_3 0x20 +#define USBHID_KEY_4 0x21 +#define USBHID_KEY_5 0x22 +#define USBHID_KEY_6 0x23 +#define USBHID_KEY_7 0x24 +#define USBHID_KEY_8 0x25 +#define USBHID_KEY_9 0x26 +#define USBHID_KEY_0 0x27 +#define USBHID_KEY_RETURN 0x28 +#define USBHID_KEY_ESC 0x29 +#define USBHID_KEY_BACKSPACE 0x2a +#define USBHID_KEY_TAB 0x2b +#define USBHID_KEY_SPACE 0x2c +#define USBHID_KEY_MINUS 0x2d +#define USBHID_KEY_EQUAL 0x2e +#define USBHID_KEY_LEFTBRACE 0x2f +#define USBHID_KEY_RIGHTBRACE 0x30 +#define USBHID_KEY_BACKSLASH 0x31 +#define USBHID_KEY_HASH 0x32 +#define USBHID_KEY_SEMICOLON 0x33 +#define USBHID_KEY_APOSTROPHE 0x34 +#define USBHID_KEY_GRAVE 0x35 +#define USBHID_KEY_COMMA 0x36 +#define USBHID_KEY_DOT 0x37 +#define USBHID_KEY_SLASH 0x38 +#define USBHID_KEY_CAPSLOCK 0x39 +#define USBHID_KEY_F1 0x3a +#define USBHID_KEY_F2 0x3b +#define USBHID_KEY_F3 0x3c +#define USBHID_KEY_F4 0x3d +#define USBHID_KEY_F5 0x3e +#define USBHID_KEY_F6 0x3f +#define USBHID_KEY_F7 0x40 +#define USBHID_KEY_F8 0x41 +#define USBHID_KEY_F9 0x42 +#define USBHID_KEY_F10 0x43 +#define USBHID_KEY_F11 0x44 +#define USBHID_KEY_F12 0x45 +#define USBHID_KEY_PRINT 0x46 +#define USBHID_KEY_SCROLLLOCK 0x47 +#define USBHID_KEY_PAUSE 0x48 +#define USBHID_KEY_INSERT 0x49 +#define USBHID_KEY_HOME 0x4a +#define USBHID_KEY_PAGEUP 0x4b +#define USBHID_KEY_DELETE 0x4c +#define USBHID_KEY_END 0x4d +#define USBHID_KEY_PAGEDOWN 0x4e +#define USBHID_KEY_RIGHT 0x4f +#define USBHID_KEY_LEFT 0x50 +#define USBHID_KEY_DOWN 0x51 +#define USBHID_KEY_UP 0x52 +#define USBHID_KEY_NUMLOCK 0x53 diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/start-ipkvm.service b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/start-ipkvm.service new file mode 100644 index 000000000..61d6cf213 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm/start-ipkvm.service @@ -0,0 +1,11 @@ +[Unit] +Description=OpenBMC ipKVM daemon +StopWhenUnneeded=false + +[Service] +Restart=always +ExecStartPre=/usr/bin/create_usbhid.sh +ExecStart=/usr/bin/env obmc-ikvm -v /dev/video0 -f 10 -k /dev/hidg0 -p /dev/hidg1 + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm_git.bb b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm_git.bb new file mode 100644 index 000000000..f08b29ce7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-graphics/obmc-ikvm/obmc-ikvm_git.bb @@ -0,0 +1,18 @@ +SUMMARY = "OpenBMC VNC server and ipKVM daemon" +DESCRIPTION = "obmc-ikvm is a vncserver for JPEG-serving V4L2 devices to allow ipKVM" +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +DEPENDS = " libvncserver sdbusplus sdbusplus-native phosphor-logging phosphor-dbus-interfaces autoconf-archive-native" + +SRC_URI = "git://github.com/openbmc/obmc-ikvm" +SRCREV = "2bc661d34abd1fda92a9d2b256ed88ca0e90d09a" + +PR = "r1" +PR_append = "+gitr${SRCPV}" + +SYSTEMD_SERVICE_${PN} += "start-ipkvm.service" + +S = "${WORKDIR}/git" + +inherit autotools pkgconfig obmc-phosphor-systemd diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control.bb b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control.bb new file mode 100644 index 000000000..c34578b7f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control.bb @@ -0,0 +1,201 @@ +SUMMARY = "Chassis Power Control service for Intel based platform" +DESCRIPTION = "Chassis Power Control service for Intel based platfrom" + +SRC_URI = "git://git@github.com/Intel-BMC/intel-chassis-control.git;protocol=ssh" +SRCREV = "371617ab2dbdf003e373876fa7d5a9ca83b1529d" + +S = "${WORKDIR}/git/services/chassis/" + +PV = "1.0+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${S}/LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +inherit cmake pkgconfig pythonnative +inherit obmc-phosphor-dbus-service + +DBUS_SERVICE_${PN} += "xyz.openbmc_project.Chassis.Control.Power@.service" +DBUS_SERVICE_${PN} += "xyz.openbmc_project.Chassis.Control.Chassis@.service" +DBUS_SERVICE_${PN} += "xyz.openbmc_project.Chassis.Buttons@.service" + +# Force the standby target to run these services +SYSD_TGT = "${SYSTEMD_DEFAULT_TARGET}" + +POWER_TMPL_CTRL = "xyz.openbmc_project.Chassis.Control.Power@.service" +#SYSD_TGT = "${SYSTEMD_DEFAULT_TARGET}" +POWER_INSTFMT_CTRL = "xyz.openbmc_project.Chassis.Control.Power@{0}.service" +POWER_FMT_CTRL = "../${POWER_TMPL_CTRL}:${SYSD_TGT}.wants/${POWER_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'POWER_FMT_CTRL', 'OBMC_HOST_INSTANCES')}" + +CHASSIS_TMPL_CTRL = "xyz.openbmc_project.Chassis.Control.Chassis@.service" +#SYSD_TGT = "${SYSTEMD_DEFAULT_TARGET}" +CHASSIS_INSTFMT_CTRL = "xyz.openbmc_project.Chassis.Control.Chassis@{0}.service" +CHASSIS_FMT_CTRL = "../${CHASSIS_TMPL_CTRL}:${SYSD_TGT}.wants/${CHASSIS_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'CHASSIS_FMT_CTRL', 'OBMC_HOST_INSTANCES')}" + +BUTTONS_TMPL_CTRL = "xyz.openbmc_project.Chassis.Buttons@.service" +#SYSD_TGT = "${SYSTEMD_DEFAULT_TARGET}" +BUTTONS_INSTFMT_CTRL = "xyz.openbmc_project.Chassis.Buttons@{0}.service" +BUTTONS_FMT_CTRL = "../${BUTTONS_TMPL_CTRL}:${SYSD_TGT}.wants/${BUTTONS_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'BUTTONS_FMT_CTRL', 'OBMC_HOST_INSTANCES')}" + +SYSTEMD_SERVICE_${PN} += " \ + obmc-host-start@.target \ + obmc-host-startmin@.target \ + obmc-host-stop@.target \ + obmc-host-reboot@.target \ + obmc-chassis-poweroff@.target \ + obmc-chassis-poweron@.target \ + obmc-chassis-hard-poweroff@.target \ + obmc-host-soft-reboot@.target \ + obmc-host-warm-reset@.target \ + obmc-chassis-powerreset@.target \ + " + +RESET_TGTFMT = "obmc-chassis-powerreset@{0}.target" + +RESET_ON_TMPL = "op-reset-chassis-running@.service" +RESET_ON_INSTFMT = "op-reset-chassis-running@{0}.service" +RESET_ON_FMT = "../${RESET_ON_TMPL}:${RESET_TGTFMT}.requires/${RESET_ON_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${RESET_ON_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'RESET_ON_FMT', 'OBMC_CHASSIS_INSTANCES')}" + +RESET_ON_CHASSIS_TMPL = "op-reset-chassis-on@.service" +RESET_ON_CHASSIS_INSTFMT = "op-reset-chassis-on@{0}.service" +RESET_ON_CHASSIS_FMT = "../${RESET_ON_CHASSIS_TMPL}:${RESET_TGTFMT}.requires/${RESET_ON_CHASSIS_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${RESET_ON_CHASSIS_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'RESET_ON_CHASSIS_FMT', 'OBMC_CHASSIS_INSTANCES')}" + +# Force the standby target to run the chassis reset check target +RESET_TMPL_CTRL = "obmc-chassis-powerreset@.target" +SYSD_TGT = "${SYSTEMD_DEFAULT_TARGET}" +RESET_INSTFMT_CTRL = "obmc-chassis-powerreset@{0}.target" +RESET_FMT_CTRL = "../${RESET_TMPL_CTRL}:${SYSD_TGT}.wants/${RESET_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'RESET_FMT_CTRL', 'OBMC_CHASSIS_INSTANCES')}" + +START_TMPL = "intel-power-start@.service" +START_TGTFMT = "obmc-chassis-poweron@{0}.target" +START_INSTFMT = "intel-power-start@{0}.service" +START_FMT = "../${START_TMPL}:${START_TGTFMT}.requires/${START_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${START_TMPL}" + +STOP_TMPL = "intel-power-stop@.service" +STOP_TGTFMT = "obmc-chassis-poweroff@{0}.target" +STOP_INSTFMT = "intel-power-stop@{0}.service" +STOP_FMT = "../${STOP_TMPL}:${STOP_TGTFMT}.requires/${STOP_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${STOP_TMPL}" + +WARM_RESET_TMPL = "intel-power-warm-reset@.service" +WARM_RESET_TGTFMT = "obmc-host-warm-reset@{0}.target" +WARM_RESET_INSTFMT = "intel-power-warm-reset@{0}.service" +WARM_RESET_FMT = "../${WARM_RESET_TMPL}:${WARM_RESET_TGTFMT}.requires/${WARM_RESET_INSTFMT}" +WARM_RESET_LINK_FMT = "obmc-host-warm-reset@.target:${WARM_RESET_TGTFMT}" +SYSTEMD_SERVICE_${PN} += "${WARM_RESET_TMPL}" + +# Build up requires relationship for START_TGTFMT and STOP_TGTFMT +SYSTEMD_LINK_${PN} += "${@compose_list(d, 'START_FMT', 'OBMC_CHASSIS_INSTANCES')}" +SYSTEMD_LINK_${PN} += "${@compose_list(d, 'STOP_FMT', 'OBMC_CHASSIS_INSTANCES')}" +SYSTEMD_LINK_${PN} += "${@compose_list(d, 'WARM_RESET_FMT', 'OBMC_CHASSIS_INSTANCES')}" +SYSTEMD_LINK_${PN} += "${@compose_list(d, 'WARM_RESET_LINK_FMT', 'OBMC_CHASSIS_INSTANCES')}" + +#The main control target requires these power targets +START_TMPL_CTRL = "obmc-chassis-poweron@.target" +START_TGTFMT_CTRL = "obmc-host-startmin@{0}.target" +START_INSTFMT_CTRL = "obmc-chassis-poweron@{0}.target" +START_FMT_CTRL = "../${START_TMPL_CTRL}:${START_TGTFMT_CTRL}.requires/${START_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list(d, 'START_FMT_CTRL', 'OBMC_CHASSIS_INSTANCES')}" + +# Chassis off requires host off +STOP_TMPL_CTRL = "obmc-host-stop@.target" +STOP_TGTFMT_CTRL = "obmc-chassis-poweroff@{0}.target" +STOP_INSTFMT_CTRL = "obmc-host-stop@{0}.target" +STOP_FMT_CTRL = "../${STOP_TMPL_CTRL}:${STOP_TGTFMT_CTRL}.requires/${STOP_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list(d, 'STOP_FMT_CTRL', 'OBMC_CHASSIS_INSTANCES')}" + +# Hard power off requires chassis off +HARD_OFF_TMPL_CTRL = "obmc-chassis-poweroff@.target" +HARD_OFF_TGTFMT_CTRL = "obmc-chassis-hard-poweroff@{0}.target" +HARD_OFF_INSTFMT_CTRL = "obmc-chassis-poweroff@{0}.target" +HARD_OFF_FMT_CTRL = "../${HARD_OFF_TMPL_CTRL}:${HARD_OFF_TGTFMT_CTRL}.requires/${HARD_OFF_INSTFMT_CTRL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'HARD_OFF_FMT_CTRL', 'OBMC_CHASSIS_INSTANCES')}" + +# Host soft reboot to run the shutdown target +HOST_SHUTDOWN_TMPL = "obmc-host-shutdown@.target" +HOST_SOFT_REBOOT_TMPL = "obmc-host-soft-reboot@.target" +HOST_SOFT_REBOOT_TGTFMT = "obmc-host-soft-reboot@{0}.target" +HOST_SHUTDOWN_INSTFMT = "obmc-host-shutdown@{0}.target" +HOST_SOFT_REBOOT_FMT = "../${HOST_SHUTDOWN_TMPL}:${HOST_SOFT_REBOOT_TGTFMT}.requires/${HOST_SHUTDOWN_INSTFMT}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'HOST_SOFT_REBOOT_FMT', 'OBMC_HOST_INSTANCES')}" +# And also to call the host startmin service +HOST_SOFT_REBOOT_SVC = "phosphor-reboot-host@.service" +HOST_SOFT_REBOOT_SVC_INST = "phosphor-reboot-host@{0}.service" +HOST_SOFT_REBOOT_SVC_FMT = "../${HOST_SOFT_REBOOT_SVC}:${HOST_SOFT_REBOOT_TGTFMT}.requires/${HOST_SOFT_REBOOT_SVC_INST}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'HOST_SOFT_REBOOT_SVC_FMT', 'OBMC_HOST_INSTANCES')}" + +#Broadcast Host state +PRE_HOST_START_TMPL = "obmc-send-signal-pre-host-start@.service" +PRE_HOST_START_TGTFMT = "obmc-host-start-pre@{0}.target" +PRE_HOST_START_INSTFMT = "obmc-send-signal-pre-host-start@{0}.service" +PRE_HOST_START_FMT = "../${PRE_HOST_START_TMPL}:${PRE_HOST_START_TGTFMT}.requires/${PRE_HOST_START_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${PRE_HOST_START_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'PRE_HOST_START_FMT', 'OBMC_HOST_INSTANCES')}" + +POST_HOST_START_TMPL = "obmc-send-signal-post-host-start@.service" +POST_HOST_START_TGTFMT = "obmc-host-started@{0}.target" +POST_HOST_START_INSTFMT = "obmc-send-signal-post-host-start@{0}.service" +POST_HOST_START_FMT = "../${POST_HOST_START_TMPL}:${POST_HOST_START_TGTFMT}.requires/${POST_HOST_START_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${POST_HOST_START_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'POST_HOST_START_FMT', 'OBMC_HOST_INSTANCES')}" + +HOST_STARTING_TMPL = "obmc-send-signal-host-starting@.service" +HOST_STARTING_TGTFMT = "obmc-host-starting@{0}.target" +HOST_STARTING_INSTFMT = "obmc-send-signal-host-starting@{0}.service" +HOST_STARTING_FMT = "../${HOST_STARTING_TMPL}:${HOST_STARTING_TGTFMT}.requires/${HOST_STARTING_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${HOST_STARTING_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'HOST_STARTING_FMT', 'OBMC_HOST_INSTANCES')}" + +PRE_HOST_STOP_TMPL = "obmc-send-signal-pre-host-stop@.service" +PRE_HOST_STOP_TGTFMT = "obmc-host-stop-pre@{0}.target" +PRE_HOST_STOP_INSTFMT = "obmc-send-signal-pre-host-stop@{0}.service" +PRE_HOST_STOP_FMT = "../${PRE_HOST_STOP_TMPL}:${PRE_HOST_STOP_TGTFMT}.requires/${PRE_HOST_STOP_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${PRE_HOST_STOP_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'PRE_HOST_STOP_FMT', 'OBMC_HOST_INSTANCES')}" + +POST_HOST_STOP_TMPL = "obmc-send-signal-post-host-stop@.service" +POST_HOST_STOP_TGTFMT = "obmc-host-stopped@{0}.target" +POST_HOST_STOP_INSTFMT = "obmc-send-signal-post-host-stop@{0}.service" +POST_HOST_STOP_FMT = "../${POST_HOST_STOP_TMPL}:${POST_HOST_STOP_TGTFMT}.requires/${POST_HOST_STOP_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${POST_HOST_STOP_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'POST_HOST_STOP_FMT', 'OBMC_HOST_INSTANCES')}" + +HOST_STOPPING_TMPL = "obmc-send-signal-host-stopping@.service" +HOST_STOPPING_TGTFMT = "obmc-host-stopping@{0}.target" +HOST_STOPPING_INSTFMT = "obmc-send-signal-host-stopping@{0}.service" +HOST_STOPPING_FMT = "../${HOST_STOPPING_TMPL}:${HOST_STOPPING_TGTFMT}.requires/${HOST_STOPPING_INSTFMT}" +SYSTEMD_SERVICE_${PN} += "${HOST_STOPPING_TMPL}" +SYSTEMD_LINK_${PN} += "${@compose_list_zip(d, 'HOST_STOPPING_FMT', 'OBMC_HOST_INSTANCES')}" + +DEPENDS += " \ + autoconf-archive-native \ + boost \ + i2c-tools \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-dbus-interfaces \ + phosphor-dbus-interfaces-native \ + phosphor-logging \ + " +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-dbus-interfaces \ + phosphor-logging \ + " + +EXTRA_OECMAKE = " -DENABLE_GTEST=OFF -DCMAKE_SKIP_RPATH=ON" + +# linux-libc-headers guides this way to include custom uapi headers +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/uapi" +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include" +do_configure[depends] += "virtual/kernel:do_shared_workdir" diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-start@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-start@.service new file mode 100644 index 000000000..763c11546 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-start@.service @@ -0,0 +1,16 @@ +[Unit] +Description=Start Power%i on +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +Conflicts=obmc-chassis-poweroff@%i.target +ConditionPathExists=!/run/openbmc/chassis@%i-on + +[Service] +Type=oneshot +ExecStart=/bin/sh -c "busctl call `mapper get-service /xyz/openbmc_project/Chassis/Control/Power%i` \ + /xyz/openbmc_project/Chassis/Control/Power%i xyz.openbmc_project.Chassis.Control.Power setPowerState i 1" +SyslogIdentifier=intel-power-start +StartLimitInterval=0 + +[Install] +WantedBy=obmc-host-start@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-stop@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-stop@.service new file mode 100644 index 000000000..5d0e46f82 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-stop@.service @@ -0,0 +1,20 @@ +[Unit] +Description=Stop Power%i +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +Conflicts=obmc-chassis-poweron@%i.target +Conflicts=obmc-host-start@%i.target + +[Service] +Type=oneshot +ExecStart=/bin/sh -c "busctl call `mapper get-service /xyz/openbmc_project/Chassis/Control/Power%i` \ + /xyz/openbmc_project/Chassis/Control/Power%i xyz.openbmc_project.Chassis.Control.Power setPowerState i 0" +SyslogIdentifier=intel-power-stop +StartLimitInterval=0 + +ExecStart=/bin/rm -f /run/openbmc/chassis@%i-on +ExecStart=/bin/rm -f /run/openbmc/host@%i-on +ExecStart=/bin/rm -f /run/openbmc/host@%i-request + +[Install] +WantedBy=obmc-chassis-poweroff@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-warm-reset@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-warm-reset@.service new file mode 100644 index 000000000..8d4897a25 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/intel-power-warm-reset@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Power%i warm reset +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +Conflicts=obmc-chassis-poweroff@%i.target + +[Service] +Type=oneshot +ExecStart=/bin/sh -c "busctl call `mapper get-service /xyz/openbmc_project/Chassis/Control/Power%i` \ + /xyz/openbmc_project/Chassis/Control/Power%i xyz.openbmc_project.Chassis.Control.Power setPowerState i 2" +SyslogIdentifier=intel-power-warm-reset + +[Install] +WantedBy=obmc-host-warm-reset@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-hard-poweroff@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-hard-poweroff@.target new file mode 100644 index 000000000..9a9902f7c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-hard-poweroff@.target @@ -0,0 +1,12 @@ +[Unit] +Description=Chassis%i (Hard Power Off) +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-chassis-poweron@%i.target +Conflicts=obmc-chassis-reset@%i.target +Conflicts=obmc-host-shutdown@%i.target +Conflicts=xyz.openbmc_project.Ipmi.Internal.SoftPowerOff.service +RefuseManualStop=yes + diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-poweroff@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-poweroff@.target new file mode 100644 index 000000000..34580a21f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-poweroff@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Chassis%i (Power Off) +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-chassis-poweron@%i.target +Conflicts=obmc-chassis-reset@%i.target +RefuseManualStop=yes + diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-poweron@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-poweron@.target new file mode 100644 index 000000000..f8fecf2a6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-poweron@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Chassis%i (Power On) +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-chassis-poweroff@%i.target +RefuseManualStop=yes +OnFailure=obmc-chassis-poweroff@%i.target +OnFailureJobMode=flush diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-powerreset@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-powerreset@.target new file mode 100644 index 000000000..8d7c47e6b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-chassis-powerreset@.target @@ -0,0 +1,7 @@ +[Unit] +Description=Chassis%i (Reset Check) +Conflicts=obmc-chassis-poweroff@%i.target +RefuseManualStop=yes + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-reboot@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-reboot@.target new file mode 100644 index 000000000..c860889e3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-reboot@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Reboot Host%i +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-host-startmin@%i.target +RefuseManualStop=yes +OnFailure=obmc-chassis-poweroff@%i.target +OnFailureJobMode=flush diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-soft-reboot@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-soft-reboot@.target new file mode 100644 index 000000000..c35c3e1ae --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-soft-reboot@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Soft Reboot Host%i +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-host-startmin@%i.target +RefuseManualStop=yes +OnFailure=obmc-chassis-poweroff@%i.target +OnFailureJobMode=flush diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-start@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-start@.target new file mode 100644 index 000000000..425953d4d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-start@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Start Host%i +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-host-stop@%i.target +RefuseManualStop=yes +OnFailure=obmc-host-quiesce@%i.target +OnFailureJobMode=flush
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-startmin@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-startmin@.target new file mode 100644 index 000000000..69056254a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-startmin@.target @@ -0,0 +1,6 @@ +[Unit] +Description=Start Host%i Minimum +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-stop@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-stop@.target new file mode 100644 index 000000000..0693db6e5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-stop@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Stop Host%i +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-host-startmin@%i.target +RefuseManualStop=yes +OnFailure=obmc-chassis-poweroff@%i.target +OnFailureJobMode=flush
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-warm-reset@.target b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-warm-reset@.target new file mode 100644 index 000000000..8aed937e7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-host-warm-reset@.target @@ -0,0 +1,10 @@ +[Unit] +Description=Warm reset Host%i +Wants={SYSTEMD_DEFAULT_TARGET} +After={SYSTEMD_DEFAULT_TARGET} +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Chassis%i.service +Conflicts=obmc-host-stop@%i.target +RefuseManualStop=yes +OnFailure=obmc-host-quiesce@%i.target +OnFailureJobMode=flush diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-host-starting@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-host-starting@.service new file mode 100644 index 000000000..4e84c8783 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-host-starting@.service @@ -0,0 +1,13 @@ +[Unit] +Description=Broadcast host starting signal to dbus +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "dbus-send --system --type=signal /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host.HostStarting" +SyslogIdentifier=hoststartingsignal + +[Install] +WantedBy=obmc-host-starting@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-host-stopping@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-host-stopping@.service new file mode 100644 index 000000000..0f89f94a8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-host-stopping@.service @@ -0,0 +1,13 @@ +[Unit] +Description=Broadcast host stopping signal to dbus +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "dbus-send --system --type=signal /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host.HostStoping" +SyslogIdentifier=hoststoppingsignal + +[Install] +WantedBy=obmc-host-stopping@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-post-host-start@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-post-host-start@.service new file mode 100644 index 000000000..f7e0a3bde --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-post-host-start@.service @@ -0,0 +1,13 @@ +[Unit] +Description=Broadcast post host start signal to dbus +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "dbus-send --system --type=signal /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host.PostHostStart" +SyslogIdentifier=posthoststartsignal + +[Install] +WantedBy=obmc-host-started@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-post-host-stop@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-post-host-stop@.service new file mode 100644 index 000000000..90007dbf2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-post-host-stop@.service @@ -0,0 +1,13 @@ +[Unit] +Description=Broadcast post host stop signal to dbus +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "dbus-send --system --type=signal /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host.PostHostStop" +SyslogIdentifier=posthoststopsignal + +[Install] +WantedBy=obmc-host-stopped@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-pre-host-start@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-pre-host-start@.service new file mode 100644 index 000000000..a57423e2c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-pre-host-start@.service @@ -0,0 +1,13 @@ +[Unit] +Description=Broadcast pre host start signal to dbus +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "dbus-send --system --type=signal /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host.PreHostStart" +SyslogIdentifier=prehoststartsignal + +[Install] +WantedBy=obmc-host-start-pre@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-pre-host-stop@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-pre-host-stop@.service new file mode 100644 index 000000000..ec6f453cd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/obmc-send-signal-pre-host-stop@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Broadcast pre host stop signal to dbus +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "dbus-send --system --type=signal /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host.PreHostStop" +SyslogIdentifier=prehoststopsignal + +[Install] +WantedBy=obmc-host-stop-pre@%i.target + diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/op-reset-chassis-on@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/op-reset-chassis-on@.service new file mode 100644 index 000000000..d3ea71639 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/op-reset-chassis-on@.service @@ -0,0 +1,15 @@ +[Unit] +Description=Start chassis%i on after BMC reset +Requires=op-reset-chassis-running@%i.service +After=op-reset-chassis-running@%i.service +After=obmc-power-reset-on@%i.target +Requires=obmc-power-reset-on@%i.target +ConditionPathExists=/run/openbmc/chassis@%i-on + +[Service] +RemainAfterExit=no +ExecStart=/bin/systemctl start obmc-host-start@%i.target + + +[Install] +WantedBy=obmc-chassis-powerreset@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/op-reset-chassis-running@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/op-reset-chassis-running@.service new file mode 100644 index 000000000..3280d0a40 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/op-reset-chassis-running@.service @@ -0,0 +1,15 @@ +[Unit] +Description=Check Chassis%i pgood and create a file to indicate it +Wants=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +After=mapper-wait@-xyz-openbmc_project-Chassis-Control-Power%i.service +Wants=obmc-power-reset-on@%i.target +Before=obmc-power-reset-on@%i.target +Conflicts=obmc-chassis-poweroff@%i.target + +[Service] +RemainAfterExit=no +Type=oneshot +ExecStart=/bin/sh -c "if [ $(busctl get-property `mapper get-service /xyz/openbmc_project/Chassis/Control/Power%i` /xyz/openbmc_project/Chassis/Control/Power%i xyz.openbmc_project.Chassis.Control.Power pgood | sed 's/i\s*[1]/on/' | grep on | wc -l) != 0 ]; then mkdir -p /run/openbmc/ && touch /run/openbmc/chassis@%i-on; fi" + +[Install] +WantedBy=obmc-chassis-powerreset@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Buttons@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Buttons@.service new file mode 100644 index 000000000..e1e3baedf --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Buttons@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Intel Buttons%i + +[Service] +Restart=always +RestartSec=3 +ExecStart=/usr/bin/env buttons +SyslogIdentifier=buttons +Type=dbus +BusName={BUSNAME} + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} + diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Control.Chassis@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Control.Chassis@.service new file mode 100644 index 000000000..521cb17b4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Control.Chassis@.service @@ -0,0 +1,15 @@ +[Unit] +Description=Intel Chassis%i Control + +[Service] +Restart=always +ExecStart=/usr/bin/env chassis-control +SyslogIdentifier=chassis-control +Type=dbus +BusName={BUSNAME} +Nice=19 +TimeoutStartSec=180s + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} + diff --git a/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Control.Power@.service b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Control.Power@.service new file mode 100644 index 000000000..985479401 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/chassis/intel-chassis-control/xyz.openbmc_project.Chassis.Control.Power@.service @@ -0,0 +1,15 @@ + +[Unit] +Description=Intel Power Control%i + +[Service] +Restart=always +RestartSec=3 +ExecStart=/usr/bin/env power-control +SyslogIdentifier=power-control +Type=dbus +BusName={BUSNAME} + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} + diff --git a/meta-openbmc-mods/meta-common/recipes-intel/images/intel-platforms.bb b/meta-openbmc-mods/meta-common/recipes-intel/images/intel-platforms.bb new file mode 100644 index 000000000..276e549b9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/images/intel-platforms.bb @@ -0,0 +1,13 @@ +DESCRIPTION = "Image with Intel content based upon Phosphor, an OpenBMC framework." + +inherit obmc-phosphor-full-fitimage +inherit obmc-phosphor-image-common +inherit obmc-phosphor-image-dev + +FEATURE_PACKAGES_obmc-sensors = "" + +fix_shadow_perms() { + chgrp shadow ${IMAGE_ROOTFS}${sysconfdir}/shadow + chmod u=rw,g+r ${IMAGE_ROOTFS}${sysconfdir}/shadow +} +ROOTFS_POSTPROCESS_COMMAND += "fix_shadow_perms ; " diff --git a/meta-openbmc-mods/meta-common/recipes-intel/packagegroups/packagegroup-intel-apps.bb b/meta-openbmc-mods/meta-common/recipes-intel/packagegroups/packagegroup-intel-apps.bb new file mode 100644 index 000000000..923133be7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/packagegroups/packagegroup-intel-apps.bb @@ -0,0 +1,43 @@ +SUMMARY = "OpenBMC for Intel - Applications" +PR = "r1" + +inherit packagegroup + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +PROVIDES = "${PACKAGES}" +PACKAGES = " \ + ${PN}-chassis \ + ${PN}-fans \ + ${PN}-flash \ + ${PN}-system \ + " + +PROVIDES += "virtual/obmc-chassis-mgmt" +PROVIDES += "virtual/obmc-fan-mgmt" +PROVIDES += "virtual/obmc-flash-mgmt" +PROVIDES += "virtual/obmc-system-mgmt" + +RPROVIDES_${PN}-chassis += "virtual-obmc-chassis-mgmt" +RPROVIDES_${PN}-fans += "virtual-obmc-fan-mgmt" +RPROVIDES_${PN}-flash += "virtual-obmc-flash-mgmt" +RPROVIDES_${PN}-system += "virtual-obmc-system-mgmt" + +SUMMARY_${PN}-chassis = "Intel Chassis" +RDEPENDS_${PN}-chassis = " \ + intel-chassis-control \ + obmc-host-failure-reboots \ + " + +SUMMARY_${PN}-fans = "Intel Fans" +RDEPENDS_${PN}-fans = " \ + phosphor-pid-control \ + " + +SUMMARY_${PN}-flash = "Intel Flash" +RDEPENDS_${PN}-flash = " \ + obmc-flash-bmc \ + obmc-mgr-download \ + obmc-control-bmc \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-intel/packagegroups/packagegroup-obmc-apps.bbappend b/meta-openbmc-mods/meta-common/recipes-intel/packagegroups/packagegroup-obmc-apps.bbappend new file mode 100644 index 000000000..db0ec4688 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/packagegroups/packagegroup-obmc-apps.bbappend @@ -0,0 +1,6 @@ +# this is here to keep obmc-mgr-system happy, +# power control should stop relying on the deprcated +# package then we can remove it obmc-mgr-inventory +RDEPENDS_${PN}-inventory += "obmc-mgr-inventory" +# this is for image signing and signature verification +RDEPENDS_${PN}-extras += "${@bb.utils.contains('IMAGE_TYPE', 'pfr', ' phosphor-image-signing', '', d)}"
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv1.bb b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv1.bb new file mode 100644 index 000000000..21299e5b6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv1.bb @@ -0,0 +1,33 @@ +SUMMARY = "SMBIOS MDR version 1 service for Intel based platform" +DESCRIPTION = "SMBIOS MDR version 1 service for Intel based platfrom" + +SRC_URI = "git://git@github.com/Intel-BMC/provingground.git;protocol=ssh" +SRCREV = "3cc86d6c536b4c5ee7afb5447837b83ce8b3d149" + +S = "${WORKDIR}/git/services/smbios/" + +PV = "1.0+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${S}/LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +inherit cmake pkgconfig pythonnative +inherit obmc-phosphor-systemd + +SYSTEMD_SERVICE_${PN} += "smbios-mdrv1.service" + +DEPENDS += " \ + autoconf-archive-native \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-dbus-interfaces \ + phosphor-dbus-interfaces-native \ + phosphor-logging \ + " +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-dbus-interfaces \ + phosphor-logging \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv1/smbios-mdrv1.service b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv1/smbios-mdrv1.service new file mode 100644 index 000000000..edfd3bf70 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv1/smbios-mdrv1.service @@ -0,0 +1,13 @@ +[Unit] +Description=Intel BMC SMBIOS MDR V1 + +[Service] +Restart=always +RestartSec=5 +StartLimitBurst=10 +ExecStartPre=/bin/mkdir -p /etc/smbios +ExecStart=/usr/bin/env smbiosapp +SyslogIdentifier=smbiosapp + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv2.bb b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv2.bb new file mode 100644 index 000000000..074f580e9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv2.bb @@ -0,0 +1,33 @@ +SUMMARY = "SMBIOS MDR version 2 service for Intel based platform" +DESCRIPTION = "SMBIOS MDR version 2 service for Intel based platfrom" + +SRC_URI = "git://git@github.com/Intel-BMC/provingground.git;protocol=ssh" +SRCREV = "3cc86d6c536b4c5ee7afb5447837b83ce8b3d149" + +S = "${WORKDIR}/git/services/smbios-mdrv2/" + +PV = "1.0+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${S}/LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +inherit cmake pkgconfig pythonnative +inherit obmc-phosphor-systemd + +SYSTEMD_SERVICE_${PN} += "smbios-mdrv2.service" + +DEPENDS += " \ + autoconf-archive-native \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-dbus-interfaces \ + phosphor-dbus-interfaces-native \ + phosphor-logging \ + " +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-dbus-interfaces \ + phosphor-logging \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv2/smbios-mdrv2.service b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv2/smbios-mdrv2.service new file mode 100644 index 000000000..b72873406 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-intel/smbios/smbios-mdrv2/smbios-mdrv2.service @@ -0,0 +1,12 @@ +[Unit] +Description=Intel BMC SMBIOS MDR V2 + +[Service] +Restart=always +RestartSec=5 +StartLimitBurst=10 +ExecStart=/usr/bin/env smbiosmdrv2app +SyslogIdentifier=smbiosmdrv2app + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch new file mode 100644 index 000000000..2a94453b3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch @@ -0,0 +1,56 @@ +From 2affc8ab570c9d1e6d6e5ecbdbeddbc5e3b15cc5 Mon Sep 17 00:00:00 2001 +From: Juston Li <juston.li@intel.com> +Date: Mon, 27 Mar 2017 11:16:00 -0700 +Subject: [PATCH] arm: dts: aspeed-g5: add espi + +Change-Id: I0b607657883619a3acefdbf344d39bf01790c4b1 +Signed-off-by: Juston Li <juston.li@intel.com> +--- + arch/arm/boot/dts/aspeed-g5.dtsi | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index e4c5de3208e0..a3c456ba3f34 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -260,13 +260,22 @@ + gpio-controller; + compatible = "aspeed,ast2500-gpio"; +- reg = <0x1e780000 0x1000>; ++ reg = <0x1e780000 0x0200>; + interrupts = <20>; + gpio-ranges = <&pinctrl 0 0 220>; + clocks = <&syscon ASPEED_CLK_APB>; + interrupt-controller; + #interrupt-cells = <2>; + }; + ++ sgpio: sgpio@1e780200 { ++ #gpio-cells = <2>; ++ gpio-controller; ++ compatible = "aspeed,ast2500-sgpio"; ++ reg = <0x1e780200 0x0100>; ++ interrupts = <40>; ++ interrupt-controller; ++ }; ++ + rtc: rtc@1e781000 { + compatible = "aspeed,ast2500-rtc"; + reg = <0x1e781000 0x18>; +@@ -342,6 +351,13 @@ + status = "disabled"; + }; + ++ espi: espi@1e6ee000 { ++ compatible = "aspeed,ast2500-espi-slave"; ++ reg = <0x1e6ee000 0x100>; ++ interrupts = <23>; ++ status = "disabled"; ++ }; ++ + lpc: lpc@1e789000 { + compatible = "aspeed,ast2500-lpc", "simple-mfd"; + reg = <0x1e789000 0x1000>; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch new file mode 100644 index 000000000..2ac429a22 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch @@ -0,0 +1,125 @@ +From 074f1c74fde88aac3a10059e4928919782cd40d6 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Mon, 4 Jun 2018 13:45:42 -0700 +Subject: [PATCH] New flash map for Intel + =================================================================== + +--- + .../boot/dts/openbmc-flash-layout-intel-128MB.dtsi | 58 ++++++++++++++++++++++ + .../boot/dts/openbmc-flash-layout-intel-64MB.dtsi | 39 +++++++++++++++ + 2 files changed, 97 insertions(+) + create mode 100644 arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi + create mode 100644 arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi + +diff --git a/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi b/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi +new file mode 100644 +index 0000000..23426ac +--- /dev/null ++++ b/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi +@@ -0,0 +1,58 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++// 128MB flash layout: PFR (active + tmp1/tmp2 + extra) ++// image with common RW partition ++ ++partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ u-boot@0 { ++ reg = <0x0 0x80000>; ++ label = "u-boot"; ++ }; ++ ++ fit-image-a@80000 { ++ reg = <0x80000 0x1b80000>; ++ label = "image-a"; ++ }; ++ ++ sofs@1c00000 { ++ reg = <0x1c00000 0x200000>; ++ label = "sofs"; ++ }; ++ ++ rwfs@1e00000 { ++ reg = <0x1e00000 0x600000>; ++ label = "rwfs"; ++ }; ++ ++ u-boot-env@2400000 { ++ reg = <0x2400000 0x20000>; ++ label = "u-boot-env"; ++ }; ++ ++ /* ++ pfr-resvd@1260000 { ++ reg = <0x2460000 0x20000>; ++ label = "pfr-resvd"; ++ }; ++ */ ++ ++ rc1@2480000 { ++ reg = <0x2480000 0x1b80000>; ++ label = "rc1"; ++ }; ++ ++ rc2@4000000 { ++ reg = <0x4000000 0x1b80000>; ++ label = "rc2"; ++ }; ++ ++ bios-staging@6000000 { ++ reg = <0x6000000 0x2000000>; ++ label = "bios-staging"; ++ }; ++}; ++ ++ +diff --git a/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi b/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi +new file mode 100644 +index 0000000..6ae8e57 +--- /dev/null ++++ b/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++// 64MB flash layout: redundant image with common RW partition ++ ++partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ u-boot@0 { ++ reg = <0x0 0x80000>; ++ label = "u-boot"; ++ }; ++ ++ fit-image-a@80000 { ++ reg = <0x80000 0x1b80000>; ++ label = "image-a"; ++ }; ++ ++ sofs@1c00000 { ++ reg = <0x1c00000 0x200000>; ++ label = "sofs"; ++ }; ++ ++ rwfs@1e00000 { ++ reg = <0x1e00000 0x600000>; ++ label = "rwfs"; ++ }; ++ ++ u-boot-env@2400000 { ++ reg = <0x2400000 0x20000>; ++ label = "u-boot-env"; ++ }; ++ ++ fit-image-b@2480000 { ++ reg = <0x2480000 0x1b80000>; ++ label = "image-b"; ++ }; ++}; ++ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch new file mode 100644 index 000000000..78824dde7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch @@ -0,0 +1,474 @@ +From 42505ffb3c24b3e7f8182af520ab1c10a3b3f3c4 Mon Sep 17 00:00:00 2001 +From: "Feist, James" <james.feist@intel.com> +Date: Mon, 5 Jun 2017 11:13:52 -0700 +Subject: [PATCH] Add ASPEED SGPIO driver. + +Port aspeed sgpio driver to OBMC Kernel and +enable it on Purley config. Based off AST sdk 4.0. + +Change-Id: I8529c3fb001ea6f93e63b269cdcdde3887a84e40 +Signed-off-by: James Feist <james.feist@linux.intel.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/gpio/Kconfig | 8 + + drivers/gpio/Makefile | 1 + + drivers/gpio/sgpio-aspeed.c | 416 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 425 insertions(+) + create mode 100644 drivers/gpio/sgpio-aspeed.c + +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 71c0ab46f216..a0485be99db7 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -124,6 +124,14 @@ config GPIO_ASPEED + help + Say Y here to support Aspeed AST2400 and AST2500 GPIO controllers. + ++config SGPIO_ASPEED ++ bool "ASPEED SGPIO support" ++ depends on ARCH_ASPEED ++ select GPIO_GENERIC ++ select GPIOLIB_IRQCHIP ++ help ++ Say Y here to support ASPEED SGPIO functionality. ++ + config GPIO_ATH79 + tristate "Atheros AR71XX/AR724X/AR913X GPIO support" + default y if ATH79 +diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile +index 1324c8f966a7..23b8d29bef70 100644 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -32,6 +32,7 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o + obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o + obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o + obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o ++obj-$(CONFIG_SGPIO_ASPEED) += sgpio-aspeed.o + obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o + obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o + obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o +diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/sgpio-aspeed.c +new file mode 100644 +index 000000000000..9c4add74602a +--- /dev/null ++++ b/drivers/gpio/sgpio-aspeed.c +@@ -0,0 +1,416 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2012-2017 ASPEED Technology Inc. ++// Copyright (c) 2018 Intel Corporation ++ ++#include <asm/mach/irq.h> ++#include <linux/bitfield.h> ++#include <linux/gpio/driver.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of_gpio.h> ++#include <linux/platform_device.h> ++ ++#ifdef ARCH_NR_GPIOS ++#undef ARCH_NR_GPIOS ++#endif ++ ++// TODO: move this to aspeed_sgpio_of_table ++#if defined(CONFIG_MACH_ASPEED_G5) ++#define GPIO_PORT_NUM 29 ++#elif defined(CONFIG_MACH_ASPEED_G4) ++#define GPIO_PORT_NUM 28 ++#endif ++ ++// TODO: fix defines ++#define GPIOS_PER_PORT 8 ++#define ARCH_NR_GPIOS (GPIOS_PER_PORT * GPIO_PORT_NUM) ++#define ASPEED_SGPIO_CTRL 0x54 ++#define SGPIO_CHAIN_CHIP_BASE ARCH_NR_GPIOS ++#define SGPIO_GROUP_NUMS 10 ++ ++#define ASPEED_VIC_NUMS 64 ++#define ASPEED_FIQ_NUMS 64 ++#define ARCH_NR_I2C 14 ++ ++#define IRQ_I2C_CHAIN_START (ASPEED_VIC_NUMS + ASPEED_FIQ_NUMS) ++#define IRQ_GPIO_CHAIN_START (IRQ_I2C_CHAIN_START + ARCH_NR_I2C) ++#define IRQ_SGPIO_CHAIN_START (IRQ_GPIO_CHAIN_START + ARCH_NR_GPIOS) ++ ++#define ASPEED_SGPIO_PINS_MASK GENMASK(9, 6) ++#define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16) ++#define ASPEED_SGPIO_ENABLE BIT(0) ++ ++struct aspeed_sgpio { ++ struct device *dev; ++ int mirq; /* master irq */ ++ void __iomem *base; ++// TODO: make below members as a struct member ++ uint index; ++ uint data_offset; ++ uint data_read_offset; ++ uint int_en_offset; ++ uint int_type_offset; ++ uint int_sts_offset; ++ uint rst_tol_offset; ++ struct gpio_chip chip; ++}; ++ ++uint aspeed_sgpio_to_irq(uint gpio) ++{ ++ return (gpio + IRQ_SGPIO_CHAIN_START); ++} ++EXPORT_SYMBOL(aspeed_sgpio_to_irq); ++ ++uint aspeed_irq_to_sgpio(uint irq) ++{ ++ return (irq - IRQ_SGPIO_CHAIN_START); ++} ++EXPORT_SYMBOL(aspeed_irq_to_sgpio); ++ ++static int aspeed_sgpio_get(struct gpio_chip *chip, unsigned offset) ++{ ++ struct aspeed_sgpio *priv = gpiochip_get_data(chip); ++ uint bit_nr = priv->index * GPIOS_PER_PORT + offset; ++ unsigned long flags; ++ u32 v; ++ ++ local_irq_save(flags); ++ ++ v = readl(priv->base + priv->data_offset); ++ v &= BIT(bit_nr); ++ ++ if (v) ++ v = 1; ++ else ++ v = 0; ++ ++ local_irq_restore(flags); ++ ++ dev_dbg(priv->dev, "%s, %s[%d]: %d\n", __func__, chip->label, ++ offset, v); ++ ++ return v; ++} ++ ++static void aspeed_sgpio_set(struct gpio_chip *chip, unsigned offset, int val) ++{ ++ struct aspeed_sgpio *priv = gpiochip_get_data(chip); ++ uint bit_nr = priv->index * GPIOS_PER_PORT + offset; ++ unsigned long flags; ++ u32 v; ++ ++ local_irq_save(flags); ++ ++ v = readl(priv->base + priv->data_read_offset); ++ ++ if (val) ++ v |= BIT(bit_nr); ++ else ++ v &= ~BIT(bit_nr); ++ ++ writel(v, priv->base + priv->data_offset); ++ ++ dev_dbg(priv->dev, "%s, %s[%d]: %d\n", __func__, chip->label, ++ offset, val); ++ ++ local_irq_restore(flags); ++} ++ ++#define SGPIO_BANK(name, index_no, data, read_data, int_en, int_type, \ ++ int_sts, rst_tol, chip_base_idx) { \ ++ .index = index_no, \ ++ .data_offset = data, \ ++ .data_read_offset = read_data, \ ++ .int_en_offset = int_en, \ ++ .int_type_offset = int_type, \ ++ .int_sts_offset = int_sts, \ ++ .rst_tol_offset = rst_tol, \ ++ .chip = { \ ++ .label = name, \ ++ .get = aspeed_sgpio_get, \ ++ .set = aspeed_sgpio_set, \ ++ .base = SGPIO_CHAIN_CHIP_BASE + chip_base_idx * 8, \ ++ .ngpio = GPIOS_PER_PORT, \ ++ }, \ ++} ++ ++// TODO: use a single priv after changing it as an array member of the priv ++static struct aspeed_sgpio aspeed_sgpio_gp[] = { ++ SGPIO_BANK("SGPIOA", 0, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 0), ++ SGPIO_BANK("SGPIOB", 1, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 1), ++ SGPIO_BANK("SGPIOC", 2, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 2), ++ SGPIO_BANK("SGPIOD", 3, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 3), ++ SGPIO_BANK("SGPIOE", 0, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 4), ++ SGPIO_BANK("SGPIOF", 1, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 5), ++ SGPIO_BANK("SGPIOG", 2, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 6), ++ SGPIO_BANK("SGPIOH", 3, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 7), ++ SGPIO_BANK("SGPIOI", 0, 0x038, 0x078, 0x03C, 0x040, 0x04C, 0x050, 8), ++ SGPIO_BANK("SGPIOJ", 1, 0x038, 0x078, 0x03C, 0x040, 0x04C, 0x050, 9), ++}; ++ ++/** ++ * We need to unmask the GPIO bank interrupt as soon as possible to avoid ++ * missing GPIO interrupts for other lines in the bank. Then we need to ++ * mask-read-clear-unmask the triggered GPIO lines in the bank to avoid missing ++ * nested interrupts for a GPIO line. If we wait to unmask individual GPIO lines ++ * in the bank after the line's interrupt handler has been run, we may miss some ++ * nested interrupts. ++ */ ++static void aspeed_sgpio_irq_handler(struct irq_desc *desc) ++{ ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct aspeed_sgpio *priv = irq_desc_get_chip_data(desc); ++ u32 isr; ++ int i; ++ ++ chained_irq_enter(chip, desc); ++ ++ isr = readl(priv->base + priv->int_sts_offset); ++ isr = (isr >> (GPIOS_PER_PORT * priv->index)) & 0xff; ++ ++ dev_dbg(priv->dev, "[%s] isr %x \n", priv->chip.label, isr); ++ ++ if (isr != 0) { ++ for (i = 0; i < GPIOS_PER_PORT; i++) { ++ if (BIT(i) & isr) ++ generic_handle_irq(i * GPIOS_PER_PORT + ++ i + IRQ_SGPIO_CHAIN_START); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void aspeed_sgpio_ack_irq(struct irq_data *d) ++{ ++ struct aspeed_sgpio *priv = irq_get_chip_data(d->irq); ++ uint sgpio_irq = (d->irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT; ++ uint bit_nr = priv->index * GPIOS_PER_PORT + sgpio_irq; ++ ++ dev_dbg(priv->dev, "irq %d: %s [%s] pin %d\n", d->irq, __func__, ++ priv->chip.label, sgpio_irq); ++ ++ writel(BIT(bit_nr), priv->base + priv->int_sts_offset); ++} ++ ++static void aspeed_sgpio_mask_irq(struct irq_data *d) ++{ ++ struct aspeed_sgpio *priv = irq_get_chip_data(d->irq); ++ uint sgpio_irq = (d->irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT; ++ uint bit_nr = priv->index * GPIOS_PER_PORT + sgpio_irq; ++ ++ /* Disable IRQ */ ++ writel(readl(priv->base + priv->int_en_offset) & ~BIT(bit_nr), ++ priv->base + priv->int_en_offset); ++ ++ dev_dbg(priv->dev, "irq %d: %s [%s] pin %d\n ", d->irq, __func__, ++ priv->chip.label, sgpio_irq); ++} ++ ++static void aspeed_sgpio_unmask_irq(struct irq_data *d) ++{ ++ struct aspeed_sgpio *priv = irq_data_get_irq_chip_data(d); ++ u32 sgpio_irq = (d->irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT; ++ uint bit_nr = priv->index * GPIOS_PER_PORT + sgpio_irq; ++ ++ /* Enable IRQ */ ++ writel(BIT(bit_nr), priv->base + priv->int_sts_offset); ++ writel(readl(priv->base + priv->int_en_offset) | BIT(bit_nr), ++ priv->base + priv->int_en_offset); ++ ++ dev_dbg(priv->dev, "irq %d: %s [%s] pin %d\n", d->irq, __func__, ++ priv->chip.label, sgpio_irq); ++} ++ ++static int aspeed_sgpio_irq_type(struct irq_data *d, unsigned int type) ++{ ++ unsigned int irq = d->irq; ++ struct aspeed_sgpio *priv = irq_get_chip_data(irq); ++ u32 sgpio_irq = (irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT; ++ u32 type0, type1, type2; ++ ++ dev_dbg(priv->dev, "irq: %d, sgpio_irq: %d , irq_type: 0x%x\n", ++ irq, sgpio_irq, type); ++ ++ if (type & ~IRQ_TYPE_SENSE_MASK) ++ return -EINVAL; ++ ++ type0 = readl(priv->base + priv->int_type_offset); ++ type1 = readl(priv->base + priv->int_type_offset + 0x04); ++ type2 = readl(priv->base + priv->int_type_offset + 0x08); ++ ++ switch (type) { ++ /* Edge rising type */ ++ case IRQ_TYPE_EDGE_RISING: ++ type0 |= BIT(sgpio_irq); ++ type1 &= ~BIT(sgpio_irq); ++ type2 &= ~BIT(sgpio_irq); ++ break; ++ /* Edge falling type */ ++ case IRQ_TYPE_EDGE_FALLING: ++ type2 |= BIT(sgpio_irq); ++ break; ++ case IRQ_TYPE_EDGE_BOTH: ++ type0 &= ~BIT(sgpio_irq); ++ type1 |= BIT(sgpio_irq); ++ type2 &= ~BIT(sgpio_irq); ++ break; ++ case IRQ_TYPE_LEVEL_HIGH: ++ type0 |= BIT(sgpio_irq); ++ type1 |= BIT(sgpio_irq); ++ type2 &= ~BIT(sgpio_irq); ++ break; ++ case IRQ_TYPE_LEVEL_LOW: ++ type0 &= ~BIT(sgpio_irq); ++ type1 |= BIT(sgpio_irq); ++ type2 &= ~BIT(sgpio_irq); ++ break; ++ default: ++ dev_dbg(priv->dev, "not supported trigger type: %d", type); ++ return -EINVAL; ++ break; ++ } ++ ++ writel(type0, priv->base + priv->int_type_offset); ++ writel(type1, priv->base + priv->int_type_offset + 0x04); ++ writel(type2, priv->base + priv->int_type_offset + 0x08); ++ ++ return 0; ++} ++ ++static struct irq_chip aspeed_sgpio_irq_chip = { ++ .name = "aspeed-sgpio", ++ .irq_ack = aspeed_sgpio_ack_irq, ++ .irq_mask = aspeed_sgpio_mask_irq, ++ .irq_unmask = aspeed_sgpio_unmask_irq, ++ .irq_set_type = aspeed_sgpio_irq_type, ++}; ++ ++static int aspeed_sgpio_config(struct aspeed_sgpio *priv) ++{ ++ /** ++ * There is a limitation that SGPIO clock division has to be larger or ++ * equal to 1. And the value of clock division read back is left shift ++ * 1 bit from actual value. ++ * ++ * GPIO254[31:16] - Serial GPIO clock division ++ * Serial GPIO clock period = period of PCLK * 2 * (GPIO254[31:16] + 1) ++ * ++ * SGPIO master controller updates every data input when SGPMLD is low. ++ * For example, SGPIO clock is 1MHz and number of SGPIO is 80 then each ++ * SGPIO will be updated in every 80us. ++ */ ++ writel(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, 10) | ++ FIELD_PREP(ASPEED_SGPIO_PINS_MASK, SGPIO_GROUP_NUMS) | ++ ASPEED_SGPIO_ENABLE, ++ priv->base + ASPEED_SGPIO_CTRL); ++ dev_dbg(priv->dev, "sgpio config reg: 0x%08X\n", ++ readl(priv->base + ASPEED_SGPIO_CTRL)); ++ ++ return 0; ++} ++ ++static int aspeed_sgpio_probe(struct platform_device *pdev) ++{ ++ int i, j; ++ uint irq; ++ struct resource *res; ++ struct aspeed_sgpio *priv; ++ void __iomem *base; ++ int mirq; ++ ++ // aspeed_scu_multi_func_sgpio(); done via pinctl ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ mirq = platform_get_irq(pdev, 0); ++ if (!mirq) ++ return -ENODEV; ++ ++ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_gp); i++) { ++ // TODO: use heap allocation and use a single priv ++ priv = &aspeed_sgpio_gp[i]; ++ priv->dev = &pdev->dev; ++ priv->base = base; ++ priv->mirq = mirq; ++ dev_set_drvdata(&pdev->dev, priv); ++ ++ dev_dbg(priv->dev, "add gpio_chip [%s]: %d\n", priv->chip.label, ++ i); ++ ++ devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); ++ ++ /* Disable Interrupt & Clear Status & Set Level-High Trigger */ ++ writel(0x00000000, priv->base + priv->int_en_offset); ++ writel(0xffffffff, priv->base + priv->int_sts_offset); ++ writel(0xffffffff, priv->base + priv->int_type_offset); ++ writel(0xffffffff, priv->base + priv->int_type_offset + 0x04); ++ writel(0x00000000, priv->base + priv->int_type_offset + 0x08); ++ ++ // TODO: no this many chip registration is needed. fix it. ++ for (j = 0; j < GPIOS_PER_PORT; j++) { ++ irq = i * GPIOS_PER_PORT + j + IRQ_SGPIO_CHAIN_START; ++ dev_dbg(priv->dev, "inst chip data %d\n", irq); ++ irq_set_chip_data(irq, priv); ++ irq_set_chip_and_handler(irq, &aspeed_sgpio_irq_chip, ++ handle_level_irq); ++ irq_clear_status_flags(irq, IRQ_NOREQUEST); ++ } ++ } ++ ++ irq_set_chained_handler(priv->mirq, aspeed_sgpio_irq_handler); ++ ++ aspeed_sgpio_config(priv); ++ ++ dev_info(&pdev->dev, "sgpio controller registered, irq %d\n", ++ priv->mirq); ++ ++ return 0; ++} ++ ++static int aspeed_sgpio_remove(struct platform_device *pdev) ++{ ++ struct aspeed_sgpio *priv = ++ &aspeed_sgpio_gp[ARRAY_SIZE(aspeed_sgpio_gp) - 1]; ++ ++ irq_set_chained_handler(priv->mirq, NULL); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_sgpio_of_table[] = { ++ { .compatible = "aspeed,ast2400-sgpio", }, ++ { .compatible = "aspeed,ast2500-sgpio", }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table); ++ ++static struct platform_driver aspeed_sgpio_driver = { ++ .probe = aspeed_sgpio_probe, ++ .remove = aspeed_sgpio_remove, ++ .driver = { ++ .name = "sgpio-aspeed", ++ .of_match_table = of_match_ptr(aspeed_sgpio_of_table), ++ }, ++}; ++ ++static int __init aspeed_sgpio_init(void) ++{ ++ return platform_driver_register(&aspeed_sgpio_driver); ++} ++subsys_initcall(aspeed_sgpio_init); ++ ++static void __exit aspeed_sgpio_exit(void) ++{ ++ platform_driver_unregister(&aspeed_sgpio_driver); ++} ++module_exit(aspeed_sgpio_exit); ++ ++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); ++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); ++MODULE_DESCRIPTION("ASPEED SGPIO driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch new file mode 100644 index 000000000..346b9e3e3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch @@ -0,0 +1,240 @@ +From f4b91f5c6723e56e106a609cdbcc8da48c56499e Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@intel.com> +Date: Wed, 16 May 2018 10:03:14 -0700 +Subject: [PATCH] SGPIO DT and pinctrl fixup + +This commit fixes DT and pinctrl for SGPIO use. + +Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 54 ++++++++++-------------------- + arch/arm/boot/dts/aspeed-g5.dtsi | 8 +++++ + drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c | 48 +++++++++++++------------- + drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c | 4 +++ + 4 files changed, 54 insertions(+), 60 deletions(-) + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index 6af12872ee74..9aed0f696a98 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -201,6 +201,18 @@ + interrupt-controller; + }; + ++ sgpio: sgpio@1e780200 { ++ #gpio-cells = <2>; ++ gpio-controller; ++ compatible = "aspeed,ast2400-sgpio"; ++ reg = <0x1e780200 0x0100>; ++ interrupts = <40>; ++ interrupt-controller; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_sgpm_default>; ++ status = "disabled"; ++ }; ++ + timer: timer@1e782000 { + /* This timer is a Faraday FTTMR010 derivative */ + compatible = "aspeed,ast2400-timer"; +@@ -1150,44 +1162,14 @@ + groups = "SD2"; + }; + +- pinctrl_sgpmck_default: sgpmck_default { +- function = "SGPMCK"; +- groups = "SGPMCK"; +- }; +- +- pinctrl_sgpmi_default: sgpmi_default { +- function = "SGPMI"; +- groups = "SGPMI"; +- }; +- +- pinctrl_sgpmld_default: sgpmld_default { +- function = "SGPMLD"; +- groups = "SGPMLD"; +- }; +- +- pinctrl_sgpmo_default: sgpmo_default { +- function = "SGPMO"; +- groups = "SGPMO"; +- }; +- +- pinctrl_sgpsck_default: sgpsck_default { +- function = "SGPSCK"; +- groups = "SGPSCK"; +- }; +- +- pinctrl_sgpsi0_default: sgpsi0_default { +- function = "SGPSI0"; +- groups = "SGPSI0"; +- }; +- +- pinctrl_sgpsi1_default: sgpsi1_default { +- function = "SGPSI1"; +- groups = "SGPSI1"; ++ pinctrl_sgpm_default: sgpm_default { ++ function = "SGPM"; ++ groups = "SGPM"; + }; + +- pinctrl_sgpsld_default: sgpsld_default { +- function = "SGPSLD"; +- groups = "SGPSLD"; ++ pinctrl_sgps_default: sgps_default { ++ function = "SGPS"; ++ groups = "SGPS"; + }; + + pinctrl_sioonctrl_default: sioonctrl_default { +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 01e901031bd4..36d72c91a2ad 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -274,6 +274,9 @@ + reg = <0x1e780200 0x0100>; + interrupts = <40>; + interrupt-controller; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_sgpm_default>; ++ status = "disabled"; + }; + + timer: timer@1e782000 { +@@ -1324,6 +1327,11 @@ + groups = "SDA2"; + }; + ++ pinctrl_sgpm_default: sgpm_default { ++ function = "SGPM"; ++ groups = "SGPM"; ++ }; ++ + pinctrl_sgps1_default: sgps1_default { + function = "SGPS1"; + groups = "SGPS1"; +diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c +index 05b153034517..353af05b8602 100644 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c +@@ -401,16 +401,22 @@ SSSF_PIN_DECL(E16, GPIOF6, TXD4, SIG_DESC_SET(SCU80, 30)); + SSSF_PIN_DECL(C17, GPIOF7, RXD4, SIG_DESC_SET(SCU80, 31)); + + #define A14 48 +-SSSF_PIN_DECL(A14, GPIOG0, SGPSCK, SIG_DESC_SET(SCU84, 0)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPSCK, SGPS, SIG_DESC_SET(SCU84, 0)); ++SS_PIN_DECL(A14, GPIOG0, SGPSCK); + + #define E13 49 +-SSSF_PIN_DECL(E13, GPIOG1, SGPSLD, SIG_DESC_SET(SCU84, 1)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPSLD, SGPS, SIG_DESC_SET(SCU84, 1)); ++SS_PIN_DECL(E13, GPIOG1, SGPSLD); + + #define D13 50 +-SSSF_PIN_DECL(D13, GPIOG2, SGPSI0, SIG_DESC_SET(SCU84, 2)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPSIO, SGPS, SIG_DESC_SET(SCU84, 2)); ++SS_PIN_DECL(D13, GPIOG2, SGPSIO); + + #define C13 51 +-SSSF_PIN_DECL(C13, GPIOG3, SGPSI1, SIG_DESC_SET(SCU84, 3)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPSI1, SGPS, SIG_DESC_SET(SCU84, 3)); ++SS_PIN_DECL(C13, GPIOG3, SGPSI1); ++ ++FUNC_GROUP_DECL(SGPS, A14, E13, D13, C13); + + #define B13 52 + SIG_EXPR_LIST_DECL_SINGLE(OSCCLK, OSCCLK, SIG_DESC_SET(SCU2C, 1)); +@@ -576,16 +582,22 @@ FUNC_GROUP_DECL(SPI1PASSTHRU, C22, G18, D19, C20, B22, G19, C18, E20); + FUNC_GROUP_DECL(VGABIOS_ROM, B22, G19, C18, E20); + + #define J5 72 +-SSSF_PIN_DECL(J5, GPIOJ0, SGPMCK, SIG_DESC_SET(SCU84, 8)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPMCK, SGPM, SIG_DESC_SET(SCU84, 8)); ++SS_PIN_DECL(J5, GPIOJ0, SGPMCK); + + #define J4 73 +-SSSF_PIN_DECL(J4, GPIOJ1, SGPMLD, SIG_DESC_SET(SCU84, 9)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPMLD, SGPM, SIG_DESC_SET(SCU84, 9)); ++SS_PIN_DECL(J4, GPIOJ1, SGPMLD); + + #define K5 74 +-SSSF_PIN_DECL(K5, GPIOJ2, SGPMO, SIG_DESC_SET(SCU84, 10)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPMO, SGPM, SIG_DESC_SET(SCU84, 10)); ++SS_PIN_DECL(K5, GPIOJ2, SGPMO); + + #define J3 75 +-SSSF_PIN_DECL(J3, GPIOJ3, SGPMI, SIG_DESC_SET(SCU84, 11)); ++SIG_EXPR_LIST_DECL_SINGLE(SGPMI, SGPM, SIG_DESC_SET(SCU84, 11)); ++SS_PIN_DECL(J3, GPIOJ3, SGPMI); ++ ++FUNC_GROUP_DECL(SGPM, J5, J4, K5, J3); + + #define T4 76 + SSSF_PIN_DECL(T4, GPIOJ4, VGAHS, SIG_DESC_SET(SCU84, 12)); +@@ -2083,14 +2095,8 @@ static const struct aspeed_pin_group aspeed_g4_groups[] = { + ASPEED_PINCTRL_GROUP(SALT4), + ASPEED_PINCTRL_GROUP(SD1), + ASPEED_PINCTRL_GROUP(SD2), +- ASPEED_PINCTRL_GROUP(SGPMCK), +- ASPEED_PINCTRL_GROUP(SGPMI), +- ASPEED_PINCTRL_GROUP(SGPMLD), +- ASPEED_PINCTRL_GROUP(SGPMO), +- ASPEED_PINCTRL_GROUP(SGPSCK), +- ASPEED_PINCTRL_GROUP(SGPSI0), +- ASPEED_PINCTRL_GROUP(SGPSI1), +- ASPEED_PINCTRL_GROUP(SGPSLD), ++ ASPEED_PINCTRL_GROUP(SGPM), ++ ASPEED_PINCTRL_GROUP(SGPS), + ASPEED_PINCTRL_GROUP(SIOONCTRL), + ASPEED_PINCTRL_GROUP(SIOPBI), + ASPEED_PINCTRL_GROUP(SIOPBO), +@@ -2238,14 +2244,8 @@ static const struct aspeed_pin_function aspeed_g4_functions[] = { + ASPEED_PINCTRL_FUNC(SALT4), + ASPEED_PINCTRL_FUNC(SD1), + ASPEED_PINCTRL_FUNC(SD2), +- ASPEED_PINCTRL_FUNC(SGPMCK), +- ASPEED_PINCTRL_FUNC(SGPMI), +- ASPEED_PINCTRL_FUNC(SGPMLD), +- ASPEED_PINCTRL_FUNC(SGPMO), +- ASPEED_PINCTRL_FUNC(SGPSCK), +- ASPEED_PINCTRL_FUNC(SGPSI0), +- ASPEED_PINCTRL_FUNC(SGPSI1), +- ASPEED_PINCTRL_FUNC(SGPSLD), ++ ASPEED_PINCTRL_FUNC(SGPM), ++ ASPEED_PINCTRL_FUNC(SGPS), + ASPEED_PINCTRL_FUNC(SIOONCTRL), + ASPEED_PINCTRL_FUNC(SIOPBI), + ASPEED_PINCTRL_FUNC(SIOPBO), +diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c +index 187abd7693cf..0c89647f166f 100644 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c +@@ -577,6 +577,8 @@ SS_PIN_DECL(N3, GPIOJ2, SGPMO); + SIG_EXPR_LIST_DECL_SINGLE(SGPMI, SGPM, SIG_DESC_SET(SCU84, 11)); + SS_PIN_DECL(N4, GPIOJ3, SGPMI); + ++FUNC_GROUP_DECL(SGPM, R2, L2, N3, N4); ++ + #define N5 76 + SIG_EXPR_LIST_DECL_SINGLE(VGAHS, VGAHS, SIG_DESC_SET(SCU84, 12)); + SIG_EXPR_LIST_DECL_SINGLE(DASHN5, DASHN5, SIG_DESC_SET(SCU94, 8)); +@@ -2127,6 +2129,7 @@ static const struct aspeed_pin_group aspeed_g5_groups[] = { + ASPEED_PINCTRL_GROUP(SD2), + ASPEED_PINCTRL_GROUP(SDA1), + ASPEED_PINCTRL_GROUP(SDA2), ++ ASPEED_PINCTRL_GROUP(SGPM), + ASPEED_PINCTRL_GROUP(SGPS1), + ASPEED_PINCTRL_GROUP(SGPS2), + ASPEED_PINCTRL_GROUP(SIOONCTRL), +@@ -2296,6 +2299,7 @@ static const struct aspeed_pin_function aspeed_g5_functions[] = { + ASPEED_PINCTRL_FUNC(SD2), + ASPEED_PINCTRL_FUNC(SDA1), + ASPEED_PINCTRL_FUNC(SDA2), ++ ASPEED_PINCTRL_FUNC(SGPM), + ASPEED_PINCTRL_FUNC(SGPS1), + ASPEED_PINCTRL_FUNC(SGPS2), + ASPEED_PINCTRL_FUNC(SIOONCTRL), +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch new file mode 100644 index 000000000..232903f2c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch @@ -0,0 +1,276 @@ +From 3b9ab062d0eb781fc767bd15ce58dc7b7990e65b Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Mon, 7 Jan 2019 09:56:10 -0800 +Subject: [PATCH] Update PECI drivers to sync with linux upstreaming version + +This commit updates PECI drivers to with linux community +upstreaming version. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/hwmon/peci-cputemp.c | 2 +- + drivers/hwmon/peci-dimmtemp.c | 2 +- + drivers/hwmon/peci-hwmon.h | 2 +- + drivers/mfd/intel-peci-client.c | 43 +++++++++++++++-------------------- + drivers/peci/peci-aspeed.c | 2 +- + drivers/peci/peci-core.c | 10 ++++---- + include/linux/mfd/intel-peci-client.h | 4 +--- + include/linux/peci.h | 2 +- + 8 files changed, 29 insertions(+), 38 deletions(-) + +diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c +index 11880c86a854..63796d883c82 100644 +--- a/drivers/hwmon/peci-cputemp.c ++++ b/drivers/hwmon/peci-cputemp.c +@@ -1,5 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/hwmon.h> + #include <linux/jiffies.h> +diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c +index 86a45a90805b..6e90d9bfeb45 100644 +--- a/drivers/hwmon/peci-dimmtemp.c ++++ b/drivers/hwmon/peci-dimmtemp.c +@@ -1,5 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/hwmon.h> + #include <linux/jiffies.h> +diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h +index 6ca1855a86bb..16e3c195094c 100644 +--- a/drivers/hwmon/peci-hwmon.h ++++ b/drivers/hwmon/peci-hwmon.h +@@ -1,5 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018 Intel Corporation */ ++/* Copyright (c) 2018-2019 Intel Corporation */ + + #ifndef __PECI_HWMON_H + #define __PECI_HWMON_H +diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c +index d53e4f1078ac..d62442438512 100644 +--- a/drivers/mfd/intel-peci-client.c ++++ b/drivers/mfd/intel-peci-client.c +@@ -1,12 +1,12 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/bitfield.h> + #include <linux/mfd/core.h> + #include <linux/mfd/intel-peci-client.h> + #include <linux/module.h> +-#include <linux/peci.h> + #include <linux/of_device.h> ++#include <linux/peci.h> + + #define CPU_ID_MODEL_MASK GENMASK(7, 4) + #define CPU_ID_FAMILY_MASK GENMASK(11, 8) +@@ -18,12 +18,6 @@ + #define LOWER_BYTE_MASK GENMASK(7, 0) + #define UPPER_BYTE_MASK GENMASK(16, 8) + +-enum cpu_gens { +- CPU_GEN_HSX = 0, /* Haswell Xeon */ +- CPU_GEN_BRX, /* Broadwell Xeon */ +- CPU_GEN_SKX, /* Skylake Xeon */ +-}; +- + static struct mfd_cell peci_functions[] = { + { .name = "peci-cputemp", }, + { .name = "peci-dimmtemp", }, +@@ -31,19 +25,19 @@ static struct mfd_cell peci_functions[] = { + }; + + static const struct cpu_gen_info cpu_gen_info_table[] = { +- [CPU_GEN_HSX] = { ++ { /* Haswell Xeon */ + .family = 6, /* Family code */ + .model = INTEL_FAM6_HASWELL_X, + .core_max = CORE_MAX_ON_HSX, + .chan_rank_max = CHAN_RANK_MAX_ON_HSX, + .dimm_idx_max = DIMM_IDX_MAX_ON_HSX }, +- [CPU_GEN_BRX] = { ++ { /* Broadwell Xeon */ + .family = 6, /* Family code */ + .model = INTEL_FAM6_BROADWELL_X, + .core_max = CORE_MAX_ON_BDX, + .chan_rank_max = CHAN_RANK_MAX_ON_BDX, + .dimm_idx_max = DIMM_IDX_MAX_ON_BDX }, +- [CPU_GEN_SKX] = { ++ { /* Skylake Xeon */ + .family = 6, /* Family code */ + .model = INTEL_FAM6_SKYLAKE_X, + .core_max = CORE_MAX_ON_SKX, +@@ -53,16 +47,17 @@ static const struct cpu_gen_info cpu_gen_info_table[] = { + + static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv) + { ++ struct device *dev = &priv->client->dev; + u32 cpu_id; + u16 family; + u8 model; +- int rc; ++ int ret; + int i; + +- rc = peci_get_cpu_id(priv->client->adapter, priv->client->addr, +- &cpu_id); +- if (rc) +- return rc; ++ ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr, ++ &cpu_id); ++ if (ret) ++ return ret; + + family = FIELD_PREP(LOWER_BYTE_MASK, + FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) | +@@ -83,11 +78,11 @@ static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv) + } + + if (!priv->gen_info) { +- dev_err(priv->dev, "Can't support this CPU: 0x%x\n", cpu_id); +- rc = -ENODEV; ++ dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id); ++ ret = -ENODEV; + } + +- return rc; ++ return ret; + } + + static int peci_client_probe(struct peci_client *client) +@@ -103,31 +98,29 @@ static int peci_client_probe(struct peci_client *client) + + dev_set_drvdata(dev, priv); + priv->client = client; +- priv->dev = dev; + cpu_no = client->addr - PECI_BASE_ADDR; + + ret = peci_client_get_cpu_gen_info(priv); + if (ret) + return ret; + +- ret = devm_mfd_add_devices(priv->dev, cpu_no, peci_functions, ++ ret = devm_mfd_add_devices(dev, cpu_no, peci_functions, + ARRAY_SIZE(peci_functions), NULL, 0, NULL); + if (ret < 0) { +- dev_err(priv->dev, "Failed to register child devices: %d\n", +- ret); ++ dev_err(dev, "Failed to register child devices: %d\n", ret); + return ret; + } + + return 0; + } + +-#ifdef CONFIG_OF ++#if IS_ENABLED(CONFIG_OF) + static const struct of_device_id peci_client_of_table[] = { + { .compatible = "intel,peci-client" }, + { } + }; + MODULE_DEVICE_TABLE(of, peci_client_of_table); +-#endif ++#endif /* CONFIG_OF */ + + static const struct peci_device_id peci_client_ids[] = { + { .name = "peci-client" }, +diff --git a/drivers/peci/peci-aspeed.c b/drivers/peci/peci-aspeed.c +index 51cb2563ceb6..2293d4e56e63 100644 +--- a/drivers/peci/peci-aspeed.c ++++ b/drivers/peci/peci-aspeed.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + // Copyright (C) 2012-2017 ASPEED Technology Inc. +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/bitfield.h> + #include <linux/clk.h> +diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c +index fac8c72dcda8..7ae05ded94bf 100644 +--- a/drivers/peci/peci-core.c ++++ b/drivers/peci/peci-core.c +@@ -1,5 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/bitfield.h> + #include <linux/crc8.h> +@@ -666,9 +666,9 @@ peci_of_match_device(const struct of_device_id *matches, + return NULL; + + return of_match_device(matches, &client->dev); +-#else ++#else /* CONFIG_OF */ + return NULL; +-#endif ++#endif /* CONFIG_OF */ + } + + static const struct peci_device_id * +@@ -1119,7 +1119,7 @@ static void peci_of_register_devices(struct peci_adapter *adapter) + + of_node_put(bus); + } +-#else ++#else /* CONFIG_OF */ + static void peci_of_register_devices(struct peci_adapter *adapter) { } + #endif /* CONFIG_OF */ + +@@ -1216,7 +1216,7 @@ static int peci_of_notify(struct notifier_block *nb, + static struct notifier_block peci_of_notifier = { + .notifier_call = peci_of_notify, + }; +-#else ++#else /* CONFIG_OF_DYNAMIC */ + extern struct notifier_block peci_of_notifier; + #endif /* CONFIG_OF_DYNAMIC */ + +diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h +index 8f6d823a59cd..dd5eb36cca75 100644 +--- a/include/linux/mfd/intel-peci-client.h ++++ b/include/linux/mfd/intel-peci-client.h +@@ -1,5 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018 Intel Corporation */ ++/* Copyright (c) 2018-2019 Intel Corporation */ + + #ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H + #define __LINUX_MFD_INTEL_PECI_CLIENT_H +@@ -58,7 +58,6 @@ struct cpu_gen_info { + /** + * struct peci_client_manager - PECI client manager information + * @client; pointer to the PECI client +- * @dev: pointer to the struct device + * @name: PECI client manager name + * @gen_info: CPU generation info of the detected CPU + * +@@ -67,7 +66,6 @@ struct cpu_gen_info { + */ + struct peci_client_manager { + struct peci_client *client; +- struct device *dev; + char name[PECI_NAME_SIZE]; + const struct cpu_gen_info *gen_info; + }; +diff --git a/include/linux/peci.h b/include/linux/peci.h +index d0e47d45d1d0..4b8be939585c 100644 +--- a/include/linux/peci.h ++++ b/include/linux/peci.h +@@ -1,5 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018 Intel Corporation */ ++/* Copyright (c) 2018-2019 Intel Corporation */ + + #ifndef __LINUX_PECI_H + #define __LINUX_PECI_H +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch new file mode 100644 index 000000000..391d6f816 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch @@ -0,0 +1,426 @@ +From 59e2471d9bf64fa7d539520ef66cf5f33c0b0e55 Mon Sep 17 00:00:00 2001 +From: Haiyue Wang <haiyue.wang@linux.intel.com> +Date: Tue, 13 Feb 2018 14:28:12 +0800 +Subject: [PATCH] i2c: slave-mqueue: add mqueue driver to receive ipmi message + +Some protocols over I2C are designed for bi-directional transferring +messages by using I2C Master Write protocol. Like the MCTP (Management +Component Transport Protocol) and IPMB (Intelligent Platform Management +Bus), they both require that the userspace can receive messages from +I2C dirvers under slave mode. + +This new slave mqueue backend is used to receive and queue messages, it +will exposes these messages to userspace by sysfs bin file. + +Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com> +--- + Documentation/i2c/slave-mqueue-backend.rst | 125 +++++++++++++++++ + drivers/i2c/Kconfig | 23 +++ + drivers/i2c/Makefile | 1 + + drivers/i2c/i2c-slave-mqueue.c | 217 +++++++++++++++++++++++++++++ + 4 files changed, 366 insertions(+) + create mode 100644 Documentation/i2c/slave-mqueue-backend.rst + create mode 100644 drivers/i2c/i2c-slave-mqueue.c + +diff --git a/Documentation/i2c/slave-mqueue-backend.rst b/Documentation/i2c/slave-mqueue-backend.rst +new file mode 100644 +index 000000000000..3966cf0ab8da +--- /dev/null ++++ b/Documentation/i2c/slave-mqueue-backend.rst +@@ -0,0 +1,125 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++===================================== ++Linux I2C slave message queue backend ++===================================== ++ ++:Author: Haiyue Wang <haiyue.wang@linux.intel.com> ++ ++Some protocols over I2C/SMBus are designed for bi-directional transferring ++messages by using I2C Master Write protocol. This requires that both sides ++of the communication have slave addresses. ++ ++Like MCTP (Management Component Transport Protocol) and IPMB (Intelligent ++Platform Management Bus), they both require that the userspace can receive ++messages from i2c dirvers under slave mode. ++ ++This I2C slave mqueue (message queue) backend is used to receive and queue ++messages from the remote i2c intelligent device; and it will add the target ++slave address (with R/W# bit is always 0) into the message at the first byte, ++so that userspace can use this byte to dispatch the messages into different ++handling modules. Also, like IPMB, the address byte is in its message format, ++it needs it to do checksum. ++ ++For messages are time related, so this backend will flush the oldest message ++to queue the newest one. ++ ++Link ++---- ++`Intelligent Platform Management Bus ++Communications Protocol Specification ++<https://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmp-spec-v1.0.pdf>`_ ++ ++`Management Component Transport Protocol (MCTP) ++SMBus/I2C Transport Binding Specification ++<https://www.dmtf.org/sites/default/files/standards/documents/DSP0237_1.1.0.pdf>`_ ++ ++How to use ++---------- ++For example, the I2C5 bus has slave address 0x10, the below command will create ++the related message queue interface: ++ ++ echo slave-mqueue 0x1010 > /sys/bus/i2c/devices/i2c-5/new_device ++ ++Then you can dump the messages like this: ++ ++ hexdump -C /sys/bus/i2c/devices/5-1010/slave-mqueue ++ ++Code Example ++------------ ++*Note: call 'lseek' before 'read', this is a requirement from kernfs' design.* ++ ++:: ++ ++ #include <sys/types.h> ++ #include <sys/stat.h> ++ #include <unistd.h> ++ #include <poll.h> ++ #include <time.h> ++ #include <fcntl.h> ++ #include <stdio.h> ++ ++ int main(int argc, char *argv[]) ++ { ++ int i, r; ++ struct pollfd pfd; ++ struct timespec ts; ++ unsigned char data[256]; ++ ++ pfd.fd = open(argv[1], O_RDONLY | O_NONBLOCK); ++ if (pfd.fd < 0) ++ return -1; ++ ++ pfd.events = POLLPRI; ++ ++ while (1) { ++ r = poll(&pfd, 1, 5000); ++ ++ if (r < 0) ++ break; ++ ++ if (r == 0 || !(pfd.revents & POLLPRI)) ++ continue; ++ ++ lseek(pfd.fd, 0, SEEK_SET); ++ r = read(pfd.fd, data, sizeof(data)); ++ if (r <= 0) ++ continue; ++ ++ clock_gettime(CLOCK_MONOTONIC, &ts); ++ printf("[%ld.%.9ld] :", ts.tv_sec, ts.tv_nsec); ++ for (i = 0; i < r; i++) ++ printf(" %02x", data[i]); ++ printf("\n"); ++ } ++ ++ close(pfd.fd); ++ ++ return 0; ++ } ++ ++Result ++------ ++*./a.out "/sys/bus/i2c/devices/5-1010/slave-mqueue"* ++ ++:: ++ ++ [10183.232500449] : 20 18 c8 2c 78 01 5b ++ [10183.479358348] : 20 18 c8 2c 78 01 5b ++ [10183.726556812] : 20 18 c8 2c 78 01 5b ++ [10183.972605863] : 20 18 c8 2c 78 01 5b ++ [10184.220124772] : 20 18 c8 2c 78 01 5b ++ [10184.467764166] : 20 18 c8 2c 78 01 5b ++ [10193.233421784] : 20 18 c8 2c 7c 01 57 ++ [10193.480273460] : 20 18 c8 2c 7c 01 57 ++ [10193.726788733] : 20 18 c8 2c 7c 01 57 ++ [10193.972781945] : 20 18 c8 2c 7c 01 57 ++ [10194.220487360] : 20 18 c8 2c 7c 01 57 ++ [10194.468089259] : 20 18 c8 2c 7c 01 57 ++ [10203.233433099] : 20 18 c8 2c 80 01 53 ++ [10203.481058715] : 20 18 c8 2c 80 01 53 ++ [10203.727610472] : 20 18 c8 2c 80 01 53 ++ [10203.974044856] : 20 18 c8 2c 80 01 53 ++ [10204.220734634] : 20 18 c8 2c 80 01 53 ++ [10204.468461664] : 20 18 c8 2c 80 01 53 ++ +diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig +index efc3354d60ae..04fb851f2c82 100644 +--- a/drivers/i2c/Kconfig ++++ b/drivers/i2c/Kconfig +@@ -118,6 +118,29 @@ if I2C_SLAVE + config I2C_SLAVE_EEPROM + tristate "I2C eeprom slave driver" + ++config I2C_SLAVE_MQUEUE_MESSAGE_SIZE ++ int "The message size of I2C mqueue slave" ++ default 120 ++ ++config I2C_SLAVE_MQUEUE_QUEUE_SIZE ++ int "The queue size of I2C mqueue slave" ++ default 32 ++ help ++ This number MUST be power of 2. ++ ++config I2C_SLAVE_MQUEUE ++ tristate "I2C mqueue (message queue) slave driver" ++ help ++ Some protocols over I2C are designed for bi-directional transferring ++ messages by using I2C Master Write protocol. This driver is used to ++ receive and queue messages from the remote I2C device. ++ ++ Userspace can get the messages by reading sysfs file that this driver ++ exposes. ++ ++ This support is also available as a module. If so, the module will be ++ called i2c-slave-mqueue. ++ + endif + + config I2C_DEBUG_CORE +diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile +index bed6ba63c983..9a31bc75a446 100644 +--- a/drivers/i2c/Makefile ++++ b/drivers/i2c/Makefile +@@ -16,5 +16,6 @@ obj-$(CONFIG_I2C_MUX) += i2c-mux.o + obj-y += algos/ busses/ muxes/ + obj-$(CONFIG_I2C_STUB) += i2c-stub.o + obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o ++obj-$(CONFIG_I2C_SLAVE_MQUEUE) += i2c-slave-mqueue.o + + ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG +diff --git a/drivers/i2c/i2c-slave-mqueue.c b/drivers/i2c/i2c-slave-mqueue.c +new file mode 100644 +index 000000000000..6014bca0ff2a +--- /dev/null ++++ b/drivers/i2c/i2c-slave-mqueue.c +@@ -0,0 +1,217 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2017 - 2018, Intel Corporation. ++ ++#include <linux/i2c.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/sysfs.h> ++ ++#define MQ_MSGBUF_SIZE CONFIG_I2C_SLAVE_MQUEUE_MESSAGE_SIZE ++#define MQ_QUEUE_SIZE CONFIG_I2C_SLAVE_MQUEUE_QUEUE_SIZE ++#define MQ_QUEUE_NEXT(x) (((x) + 1) & (MQ_QUEUE_SIZE - 1)) ++ ++struct mq_msg { ++ int len; ++ u8 *buf; ++}; ++ ++struct mq_queue { ++ struct bin_attribute bin; ++ struct kernfs_node *kn; ++ ++ spinlock_t lock; /* spinlock for queue index handling */ ++ int in; ++ int out; ++ ++ struct mq_msg *curr; ++ int truncated; /* drop current if truncated */ ++ struct mq_msg *queue; ++}; ++ ++static int i2c_slave_mqueue_callback(struct i2c_client *client, ++ enum i2c_slave_event event, u8 *val) ++{ ++ struct mq_queue *mq = i2c_get_clientdata(client); ++ struct mq_msg *msg = mq->curr; ++ int ret = 0; ++ ++ switch (event) { ++ case I2C_SLAVE_WRITE_REQUESTED: ++ mq->truncated = 0; ++ ++ msg->len = 1; ++ msg->buf[0] = client->addr << 1; ++ break; ++ ++ case I2C_SLAVE_WRITE_RECEIVED: ++ if (msg->len < MQ_MSGBUF_SIZE) { ++ msg->buf[msg->len++] = *val; ++ } else { ++ dev_err(&client->dev, "message is truncated!\n"); ++ mq->truncated = 1; ++ ret = -EINVAL; ++ } ++ break; ++ ++ case I2C_SLAVE_STOP: ++ if (unlikely(mq->truncated || msg->len < 2)) ++ break; ++ ++ spin_lock(&mq->lock); ++ mq->in = MQ_QUEUE_NEXT(mq->in); ++ mq->curr = &mq->queue[mq->in]; ++ mq->curr->len = 0; ++ ++ /* Flush the oldest message */ ++ if (mq->out == mq->in) ++ mq->out = MQ_QUEUE_NEXT(mq->out); ++ spin_unlock(&mq->lock); ++ ++ kernfs_notify(mq->kn); ++ break; ++ ++ default: ++ *val = 0xFF; ++ break; ++ } ++ ++ return ret; ++} ++ ++static ssize_t i2c_slave_mqueue_bin_read(struct file *filp, ++ struct kobject *kobj, ++ struct bin_attribute *attr, ++ char *buf, loff_t pos, size_t count) ++{ ++ struct mq_queue *mq; ++ struct mq_msg *msg; ++ unsigned long flags; ++ bool more = false; ++ ssize_t ret = 0; ++ ++ mq = dev_get_drvdata(container_of(kobj, struct device, kobj)); ++ ++ spin_lock_irqsave(&mq->lock, flags); ++ if (mq->out != mq->in) { ++ msg = &mq->queue[mq->out]; ++ ++ if (msg->len <= count) { ++ ret = msg->len; ++ memcpy(buf, msg->buf, ret); ++ } else { ++ ret = -EOVERFLOW; /* Drop this HUGE one. */ ++ } ++ ++ mq->out = MQ_QUEUE_NEXT(mq->out); ++ if (mq->out != mq->in) ++ more = true; ++ } ++ spin_unlock_irqrestore(&mq->lock, flags); ++ ++ if (more) ++ kernfs_notify(mq->kn); ++ ++ return ret; ++} ++ ++static int i2c_slave_mqueue_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct device *dev = &client->dev; ++ struct mq_queue *mq; ++ int ret, i; ++ void *buf; ++ ++ mq = devm_kzalloc(dev, sizeof(*mq), GFP_KERNEL); ++ if (!mq) ++ return -ENOMEM; ++ ++ BUILD_BUG_ON(!is_power_of_2(MQ_QUEUE_SIZE)); ++ ++ buf = devm_kmalloc_array(dev, MQ_QUEUE_SIZE, MQ_MSGBUF_SIZE, ++ GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ mq->queue = devm_kzalloc(dev, sizeof(*mq->queue) * MQ_QUEUE_SIZE, ++ GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ for (i = 0; i < MQ_QUEUE_SIZE; i++) ++ mq->queue[i].buf = buf + i * MQ_MSGBUF_SIZE; ++ ++ i2c_set_clientdata(client, mq); ++ ++ spin_lock_init(&mq->lock); ++ mq->curr = &mq->queue[0]; ++ ++ sysfs_bin_attr_init(&mq->bin); ++ mq->bin.attr.name = "slave-mqueue"; ++ mq->bin.attr.mode = 0400; ++ mq->bin.read = i2c_slave_mqueue_bin_read; ++ mq->bin.size = MQ_MSGBUF_SIZE * MQ_QUEUE_SIZE; ++ ++ ret = sysfs_create_bin_file(&dev->kobj, &mq->bin); ++ if (ret) ++ return ret; ++ ++ mq->kn = kernfs_find_and_get(dev->kobj.sd, mq->bin.attr.name); ++ if (!mq->kn) { ++ sysfs_remove_bin_file(&dev->kobj, &mq->bin); ++ return -EFAULT; ++ } ++ ++ ret = i2c_slave_register(client, i2c_slave_mqueue_callback); ++ if (ret) { ++ kernfs_put(mq->kn); ++ sysfs_remove_bin_file(&dev->kobj, &mq->bin); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int i2c_slave_mqueue_remove(struct i2c_client *client) ++{ ++ struct mq_queue *mq = i2c_get_clientdata(client); ++ ++ i2c_slave_unregister(client); ++ ++ kernfs_put(mq->kn); ++ sysfs_remove_bin_file(&client->dev.kobj, &mq->bin); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id i2c_slave_mqueue_id[] = { ++ { "slave-mqueue", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, i2c_slave_mqueue_id); ++ ++#if IS_ENABLED(CONFIG_OF) ++static const struct of_device_id i2c_slave_mqueue_of_match[] = { ++ { .compatible = "slave-mqueue", .data = (void *)0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, i2c_slave_mqueue_of_match); ++#endif ++ ++static struct i2c_driver i2c_slave_mqueue_driver = { ++ .driver = { ++ .name = "i2c-slave-mqueue", ++ .of_match_table = of_match_ptr(i2c_slave_mqueue_of_match), ++ }, ++ .probe = i2c_slave_mqueue_probe, ++ .remove = i2c_slave_mqueue_remove, ++ .id_table = i2c_slave_mqueue_id, ++}; ++module_i2c_driver(i2c_slave_mqueue_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); ++MODULE_DESCRIPTION("I2C slave mode for receiving and queuing messages"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch new file mode 100644 index 000000000..fe50c0aea --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch @@ -0,0 +1,565 @@ +From 4084484a57d9a81b6581455ff144fc4f9c603075 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@intel.com> +Date: Mon, 13 Nov 2017 16:29:44 +0800 +Subject: [PATCH] Aspeed LPC SIO driver + +Add lpc sio device driver for AST2500/2400 + +Signed-off-by: Yong Li <yong.b.li@intel.com> +--- + .../devicetree/bindings/misc/aspeed-sio.txt | 14 + + drivers/misc/Kconfig | 9 + + drivers/misc/Makefile | 1 + + drivers/misc/aspeed-lpc-sio.c | 435 +++++++++++++++++++++ + include/uapi/linux/aspeed-lpc-sio.h | 44 +++ + 5 files changed, 503 insertions(+) + create mode 100644 Documentation/devicetree/bindings/misc/aspeed-sio.txt + create mode 100644 drivers/misc/aspeed-lpc-sio.c + create mode 100644 include/uapi/linux/aspeed-lpc-sio.h + +diff --git a/Documentation/devicetree/bindings/misc/aspeed-sio.txt b/Documentation/devicetree/bindings/misc/aspeed-sio.txt +new file mode 100644 +index 000000000000..7953cd3367df +--- /dev/null ++++ b/Documentation/devicetree/bindings/misc/aspeed-sio.txt +@@ -0,0 +1,14 @@ ++* Aspeed LPC SIO driver. ++ ++Required properties: ++- compatible: "aspeed,ast2500-lpc-sio" ++ - aspeed,ast2500-lpc-sio: Aspeed AST2500 family ++- reg: Should contain lpc-sio registers location and length ++ ++Example: ++lpc_sio: lpc-sio@100 { ++ compatible = "aspeed,ast2500-lpc-sio"; ++ reg = <0x100 0x20>; ++ status = "disabled"; ++}; ++ +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 689d07ea7ded..fe1e2a4072a8 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -493,6 +493,15 @@ config ASPEED_LPC_CTRL + ioctl()s, the driver also provides a read/write interface to a BMC ram + region where the host LPC read/write region can be buffered. + ++config ASPEED_LPC_SIO ++ depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON ++ tristate "Aspeed ast2400/2500 HOST LPC SIO support" ++ help ++ Provides a driver to control the LPC SIO interface ++ on ASPEED platform ++ through ++ ioctl()s. ++ + config ASPEED_LPC_SNOOP + tristate "Aspeed ast2500 HOST LPC snoop support" + depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index e4170f62ab98..a2b85ec21d09 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -56,6 +56,7 @@ obj-$(CONFIG_CXL_BASE) += cxl/ + obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o + obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o + obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o ++obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o + obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o + obj-$(CONFIG_OCXL) += ocxl/ + obj-$(CONFIG_MISC_RTSX) += cardreader/ +diff --git a/drivers/misc/aspeed-lpc-sio.c b/drivers/misc/aspeed-lpc-sio.c +new file mode 100644 +index 000000000000..fd9a83bd66d7 +--- /dev/null ++++ b/drivers/misc/aspeed-lpc-sio.c +@@ -0,0 +1,435 @@ ++/* ++ * Copyright (C) 2012-2020 ASPEED Technology Inc. ++ * Copyright (c) 2017 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++#include <linux/mfd/syscon.h> ++#include <linux/miscdevice.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/of_address.h> ++#include <linux/platform_device.h> ++#include <linux/poll.h> ++#include <linux/regmap.h> ++ ++#include <linux/aspeed-lpc-sio.h> ++ ++#define SOC_NAME "aspeed" ++#define DEVICE_NAME "lpc-sio" ++ ++#define AST_LPC_SWCR0300 0x0 ++#define LPC_PWRGD_STS (1 << 30) ++#define LPC_PWRGD_RISING_EVT_STS (1 << 29) ++#define LPC_PWRGD_FALLING_EVT_STS (1 << 28) ++#define LPC_PWRBTN_STS (1 << 27) ++#define LPC_PWRBTN_RISING_EVT_STS (1 << 26) ++#define LPC_PWRBTN_FALLING_EVT_STS (1 << 25) ++#define LPC_S5N_STS (1 << 21) ++#define LPC_S5N_RISING_EVT_STS (1 << 20) ++#define LPC_S5N_FALLING_EVT_STS (1 << 19) ++#define LPC_S3N_STS (1 << 18) ++#define LPC_S3N_RISING_EVT_STS (1 << 17) ++#define LPC_S3N_FALLING_EVT_STS (1 << 16) ++#define LPC_PWBTO_RAW_STS (1 << 15) ++#define LPC_LAST_ONCTL_STS (1 << 14) ++#define LPC_WAS_PFAIL_STS (1 << 13) ++#define LPC_POWER_UP_FAIL_STS (1 << 12) /* Crowbar */ ++#define LPC_PWRBTN_OVERRIDE_STS (1 << 11) ++ ++#define AST_LPC_SWCR0704 0x4 ++ ++#define AST_LPC_SWCR0B08 0x8 ++#define LPC_PWREQ_OUTPUT_LEVEL (1 << 25) ++#define LPC_PWBTO_OUTPUT_LEVEL (1 << 24) ++#define LPC_ONCTL_STS (1 << 15) ++#define LPC_ONCTL_GPIO_LEVEL (1 << 14) ++#define LPC_ONCTL_EN_GPIO_OUTPUT (1 << 13) ++#define LPC_ONCTL_EN_GPIO_MODE (1 << 12) ++ ++#define AST_LPC_SWCR0F0C 0xC ++#define AST_LPC_SWCR1310 0x10 ++#define AST_LPC_SWCR1714 0x14 ++#define AST_LPC_SWCR1B18 0x18 ++#define AST_LPC_SWCR1F1C 0x1C ++#define AST_LPC_ACPIE3E0 0x20 ++#define AST_LPC_ACPIC1C0 0x24 ++#define AST_LPC_ACPIB3B0 0x28 ++#define AST_LPC_ACPIB7B4 0x2C ++ ++struct aspeed_lpc_sio { ++ struct miscdevice miscdev; ++ struct regmap *regmap; ++ struct semaphore lock; ++ unsigned int reg_base; ++}; ++ ++static struct aspeed_lpc_sio *file_aspeed_lpc_sio(struct file *file) ++{ ++ return container_of(file->private_data, struct aspeed_lpc_sio, ++ miscdev); ++} ++ ++static int aspeed_lpc_sio_open(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++#define LPC_SLP3N5N_EVENT_STATUS (\ ++ LPC_S5N_RISING_EVT_STS | \ ++ LPC_S5N_FALLING_EVT_STS | \ ++ LPC_S3N_RISING_EVT_STS | \ ++ LPC_S3N_FALLING_EVT_STS) ++/************************************* ++ * SLPS3n SLPS5n State ++ * --------------------------------- ++ * 1 1 S12 ++ * 0 1 S3I ++ * x 0 S45 ++ ************************************* ++ */ ++ ++static long sio_get_acpi_state(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ u32 val; ++ int rc; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ rc = regmap_read(lpc_sio->regmap, reg, &val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ ++ /* update the ACPI state event status */ ++ if (sio_data->param != 0) { ++ if (val & LPC_SLP3N5N_EVENT_STATUS) { ++ sio_data->param = 1; ++ rc = regmap_write(lpc_sio->regmap, reg, ++ LPC_SLP3N5N_EVENT_STATUS); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_write() failed with %d(reg:0x%x)\n", ++ rc, reg); ++ return rc; ++ } ++ } else { ++ sio_data->param = 0; ++ } ++ } ++ ++ if ((val & LPC_S3N_STS) && (val & LPC_S5N_STS)) ++ sio_data->data = ACPI_STATE_S12; ++ else if ((val & LPC_S3N_STS) == 0 && (val & LPC_S5N_STS)) ++ sio_data->data = ACPI_STATE_S3I; ++ else ++ sio_data->data = ACPI_STATE_S45; ++ ++ return 0; ++} ++ ++#define LPC_PWRGD_EVENT_STATUS ( \ ++ LPC_PWRGD_RISING_EVT_STS | \ ++ LPC_PWRGD_FALLING_EVT_STS) ++ ++static long sio_get_pwrgd_status(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ u32 val; ++ int rc; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ rc = regmap_read(lpc_sio->regmap, reg, &val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ ++ /* update the PWRGD event status */ ++ if (sio_data->param != 0) { ++ if (val & LPC_PWRGD_EVENT_STATUS) { ++ sio_data->param = 1; ++ rc = regmap_write(lpc_sio->regmap, reg, ++ LPC_PWRGD_EVENT_STATUS); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_write() failed with %d(reg:0x%x)\n", ++ rc, reg); ++ return rc; ++ } ++ } else { ++ sio_data->param = 0; ++ } ++ } ++ ++ sio_data->data = (val & LPC_PWRGD_STS) != 0 ? 1 : 0; ++ ++ return 0; ++} ++ ++static long sio_get_onctl_status(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ u32 val; ++ int rc; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0B08; ++ rc = regmap_read(lpc_sio->regmap, reg, &val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ ++ sio_data->data = (val & LPC_ONCTL_STS) != 0 ? 1 : 0; ++ ++ return 0; ++} ++ ++static long sio_set_onctl_gpio(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ u32 val; ++ int rc; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0B08; ++ rc = regmap_read(lpc_sio->regmap, reg, &val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ ++ /* Enable ONCTL GPIO mode */ ++ if (sio_data->param != 0) { ++ val |= LPC_ONCTL_EN_GPIO_MODE; ++ val |= LPC_ONCTL_EN_GPIO_OUTPUT; ++ ++ if (sio_data->data != 0) ++ val |= LPC_ONCTL_GPIO_LEVEL; ++ else ++ val &= ~LPC_ONCTL_GPIO_LEVEL; ++ ++ rc = regmap_write(lpc_sio->regmap, reg, val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_write() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ } else { ++ val &= ~LPC_ONCTL_EN_GPIO_MODE; ++ rc = regmap_write(lpc_sio->regmap, reg, val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_write() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++static long sio_get_pwrbtn_override(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ u32 val; ++ int rc; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ rc = regmap_read(lpc_sio->regmap, reg, &val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ ++ /* clear the PWRBTN OVERRIDE status */ ++ if (sio_data->param != 0) { ++ if (val & LPC_PWRBTN_OVERRIDE_STS) { ++ rc = regmap_write(lpc_sio->regmap, reg, ++ LPC_PWRBTN_OVERRIDE_STS); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_write() failed with %d(reg:0x%x)\n", ++ rc, reg); ++ return rc; ++ } ++ } ++ } ++ ++ sio_data->data = (val & LPC_PWRBTN_OVERRIDE_STS) != 0 ? 1 : 0; ++ ++ return 0; ++} ++ ++static long sio_get_pfail_status(struct aspeed_lpc_sio *lpc_sio, ++ struct sio_ioctl_data *sio_data) ++{ ++ u32 reg; ++ u32 val; ++ int rc; ++ ++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300; ++ rc = regmap_read(lpc_sio->regmap, reg, &val); ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ ++ /* [ASPEED]: SWCR_03_00[13] (Was_pfail: default 1) is used to identify ++ * this current booting is from AC loss (not DC loss) if FW cleans this ++ * bit after booting successfully every time. ++ **********************************************************************/ ++ if (val & LPC_WAS_PFAIL_STS) { ++ rc = regmap_write(lpc_sio->regmap, reg, 0); /* W0C */ ++ if (rc) { ++ dev_err(lpc_sio->miscdev.parent, ++ "regmap_write() failed with %d(reg:0x%x)\n", rc, reg); ++ return rc; ++ } ++ sio_data->data = 1; ++ } else { ++ sio_data->data = 0; ++ } ++ ++ return 0; ++} ++ ++typedef long (*sio_cmd_fn) (struct aspeed_lpc_sio *sio_dev, ++ struct sio_ioctl_data *sio_data); ++static sio_cmd_fn sio_cmd_handle[SIO_MAX_CMD] = { ++ [SIO_GET_ACPI_STATE] = sio_get_acpi_state, ++ [SIO_GET_PWRGD_STATUS] = sio_get_pwrgd_status, ++ [SIO_GET_ONCTL_STATUS] = sio_get_onctl_status, ++ [SIO_SET_ONCTL_GPIO] = sio_set_onctl_gpio, ++ [SIO_GET_PWRBTN_OVERRIDE] = sio_get_pwrbtn_override, ++ [SIO_GET_PFAIL_STATUS] = sio_get_pfail_status, ++}; ++ ++static long aspeed_lpc_sio_ioctl(struct file *file, unsigned int cmd, ++ unsigned long param) ++{ ++ struct aspeed_lpc_sio *lpc_sio = file_aspeed_lpc_sio(file); ++ long ret; ++ sio_cmd_fn cmd_fn; ++ struct sio_ioctl_data sio_data; ++ ++ ++ if (copy_from_user(&sio_data, (void __user *)param, sizeof(sio_data))) ++ return -EFAULT; ++ ++ if (cmd != SIO_IOC_COMMAND || sio_data.sio_cmd >= SIO_MAX_CMD) ++ return -EINVAL; ++ ++ cmd_fn = sio_cmd_handle[sio_data.sio_cmd]; ++ if (cmd_fn == NULL) ++ return -EINVAL; ++ ++ if (down_interruptible(&lpc_sio->lock) != 0) ++ return -ERESTARTSYS; ++ ++ ret = cmd_fn(lpc_sio, &sio_data); ++ if (ret == 0) { ++ if (copy_to_user((void __user *)param, &sio_data, ++ sizeof(sio_data))) ++ ret = -EFAULT; ++ } ++ ++ up(&lpc_sio->lock); ++ ++ return ret; ++} ++ ++static const struct file_operations aspeed_lpc_sio_fops = { ++ .owner = THIS_MODULE, ++ .open = aspeed_lpc_sio_open, ++ .unlocked_ioctl = aspeed_lpc_sio_ioctl, ++}; ++ ++static int aspeed_lpc_sio_probe(struct platform_device *pdev) ++{ ++ struct aspeed_lpc_sio *lpc_sio; ++ struct device *dev; ++ int rc; ++ ++ dev = &pdev->dev; ++ ++ lpc_sio = devm_kzalloc(dev, sizeof(*lpc_sio), GFP_KERNEL); ++ if (!lpc_sio) ++ return -ENOMEM; ++ ++ dev_set_drvdata(&pdev->dev, lpc_sio); ++ ++ rc = of_property_read_u32(dev->of_node, "reg", &lpc_sio->reg_base); ++ if (rc) { ++ dev_err(dev, "Couldn't read reg device-tree property\n"); ++ return rc; ++ } ++ ++ lpc_sio->regmap = syscon_node_to_regmap( ++ pdev->dev.parent->of_node); ++ if (IS_ERR(lpc_sio->regmap)) { ++ dev_err(dev, "Couldn't get regmap\n"); ++ return -ENODEV; ++ } ++ ++ sema_init(&lpc_sio->lock, 1); ++ ++ lpc_sio->miscdev.minor = MISC_DYNAMIC_MINOR; ++ lpc_sio->miscdev.name = DEVICE_NAME; ++ lpc_sio->miscdev.fops = &aspeed_lpc_sio_fops; ++ lpc_sio->miscdev.parent = dev; ++ rc = misc_register(&lpc_sio->miscdev); ++ if (rc) ++ dev_err(dev, "Unable to register device\n"); ++ else ++ dev_info(dev, "Loaded at %pap (0x%08x)\n", ++ &lpc_sio->regmap, lpc_sio->reg_base); ++ ++ return rc; ++} ++ ++static int aspeed_lpc_sio_remove(struct platform_device *pdev) ++{ ++ struct aspeed_lpc_sio *lpc_sio = dev_get_drvdata(&pdev->dev); ++ ++ misc_deregister(&lpc_sio->miscdev); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_lpc_sio_match[] = { ++ { .compatible = "aspeed,ast2500-lpc-sio" }, ++ { }, ++}; ++ ++static struct platform_driver aspeed_lpc_sio_driver = { ++ .driver = { ++ .name = SOC_NAME "-" DEVICE_NAME, ++ .of_match_table = aspeed_lpc_sio_match, ++ }, ++ .probe = aspeed_lpc_sio_probe, ++ .remove = aspeed_lpc_sio_remove, ++}; ++ ++module_platform_driver(aspeed_lpc_sio_driver); ++ ++MODULE_DEVICE_TABLE(of, aspeed_lpc_sio_match); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); ++MODULE_AUTHOR("Yong Li <yong.blli@linux.intel.com>"); ++MODULE_DESCRIPTION("ASPEED AST LPC SIO device driver"); +diff --git a/include/uapi/linux/aspeed-lpc-sio.h b/include/uapi/linux/aspeed-lpc-sio.h +new file mode 100644 +index 000000000000..5dc1efd4a426 +--- /dev/null ++++ b/include/uapi/linux/aspeed-lpc-sio.h +@@ -0,0 +1,44 @@ ++/* ++ * Copyright (C) 2012-2020 ASPEED Technology Inc. ++ * Copyright (c) 2017 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++#ifndef _UAPI_LINUX_ASPEED_LPC_SIO_H ++#define _UAPI_LINUX_ASPEED_LPC_SIO_H ++ ++#include <linux/ioctl.h> ++ ++enum ACPI_SLP_STATE { ++ ACPI_STATE_S12 = 1, ++ ACPI_STATE_S3I, ++ ACPI_STATE_S45 ++}; ++ ++/* SWC & ACPI for SuperIO IOCTL */ ++enum SIO_CMD { ++ SIO_GET_ACPI_STATE = 0, ++ SIO_GET_PWRGD_STATUS, ++ SIO_GET_ONCTL_STATUS, ++ SIO_SET_ONCTL_GPIO, ++ SIO_GET_PWRBTN_OVERRIDE, ++ SIO_GET_PFAIL_STATUS, /* Start from AC Loss */ ++ ++ SIO_MAX_CMD ++}; ++ ++struct sio_ioctl_data { ++ unsigned short sio_cmd; ++ unsigned short param; ++ unsigned int data; ++}; ++ ++#define SIO_IOC_BASE 'P' ++#define SIO_IOC_COMMAND _IOWR(SIO_IOC_BASE, 1, struct sio_ioctl_data) ++ ++#endif /* _UAPI_LINUX_ASPEED_LPC_SIO_H */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch new file mode 100644 index 000000000..120adbbc8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch @@ -0,0 +1,597 @@ +From a01815b4bb983ede71993d6c761dedd22d148b6b Mon Sep 17 00:00:00 2001 +From: Haiyue Wang <haiyue.wang@linux.intel.com> +Date: Sat, 24 Feb 2018 11:12:32 +0800 +Subject: [PATCH] eSPI: add ASPEED AST2500 eSPI driver to boot a host with PCH + runs on eSPI + +When PCH works under eSPI mode, the PMC (Power Management Controller) in +PCH is waiting for SUS_ACK from BMC after it alerts SUS_WARN. It is in +dead loop if no SUS_ACK assert. This is the basic requirement for the BMC +works as eSPI slave. + +Also for the host power on / off actions, from BMC side, the following VW +(Virtual Wire) messages are done in firmware: +1. SLAVE_BOOT_LOAD_DONE / SLAVE_BOOT_LOAD_STATUS +2. SUS_ACK +3. OOB_RESET_ACK +4. HOST_RESET_ACK + +Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com> +--- + .../devicetree/bindings/misc/aspeed,espi-slave.txt | 20 ++ + Documentation/misc-devices/espi-slave.rst | 119 +++++++ + arch/arm/boot/dts/aspeed-g5.dtsi | 4 + + drivers/misc/Kconfig | 8 + + drivers/misc/Makefile | 1 + + drivers/misc/aspeed-espi-slave.c | 353 +++++++++++++++++++++ + 6 files changed, 505 insertions(+) + create mode 100644 Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt + create mode 100644 Documentation/misc-devices/espi-slave.rst + create mode 100644 drivers/misc/aspeed-espi-slave.c + +diff --git a/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt b/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt +new file mode 100644 +index 000000000000..4f5d47ecc882 +--- /dev/null ++++ b/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt +@@ -0,0 +1,20 @@ ++ASPEED eSPI Slave Controller ++ ++Required properties: ++ - compatible: must be one of: ++ - "aspeed,ast2500-espi-slave" ++ ++ - reg: physical base address of the controller and length of memory mapped ++ region ++ ++ - interrupts: interrupt generated by the controller ++ ++Example: ++ ++ espi: espi@1e6ee000 { ++ compatible = "aspeed,ast2500-espi-slave"; ++ reg = <0x1e6ee000 0x100>; ++ interrupts = <23>; ++ status = "disabled"; ++}; ++ +diff --git a/Documentation/misc-devices/espi-slave.rst b/Documentation/misc-devices/espi-slave.rst +new file mode 100644 +index 000000000000..185acd71bd26 +--- /dev/null ++++ b/Documentation/misc-devices/espi-slave.rst +@@ -0,0 +1,119 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++========== ++eSPI Slave ++========== ++ ++:Author: Haiyue Wang <haiyue.wang@linux.intel.com> ++ ++The PCH (**eSPI master**) provides the eSPI to support connection of a ++BMC (**eSPI slave**) to the platform. ++ ++The LPC and eSPI interfaces are mutually exclusive. Both use the same ++pins, but on power-up, a HW strap determines if the eSPI or the LPC bus ++is operational. Once selected, it’s not possible to change to the other ++interface. ++ ++``eSPI Channels and Supported Transactions`` ++ +------+---------------------+----------------------+--------------------+ ++ | CH # | Channel | Posted Cycles | Non-Posted Cycles | ++ +======+=====================+======================+====================+ ++ | 0 | Peripheral | Memory Write, | Memory Read, | ++ | | | Completions | I/O Read/Write | ++ +------+---------------------+----------------------+--------------------+ ++ | 1 | Virtual Wire | Virtual Wire GET/PUT | N/A | ++ +------+---------------------+----------------------+--------------------+ ++ | 2 | Out-of-Band Message | SMBus Packet GET/PUT | N/A | ++ +------+---------------------+----------------------+--------------------+ ++ | 3 | Flash Access | N/A | Flash Read, Write, | ++ | | | | Erase | ++ +------+---------------------+----------------------+--------------------+ ++ | N/A | General | Register Accesses | N/A | ++ +------+---------------------+----------------------+--------------------+ ++ ++Virtual Wire Channel (Channel 1) Overview ++----------------------------------------- ++ ++The Virtual Wire channel uses a standard message format to communicate ++several types of signals between the components on the platform:: ++ ++ - Sideband and GPIO Pins: System events and other dedicated signals ++ between the PCH and eSPI slave. These signals are tunneled between the ++ two components over eSPI. ++ ++ - Serial IRQ Interrupts: Interrupts are tunneled from the eSPI slave to ++ the PCH. Both edge and triggered interrupts are supported. ++ ++When PCH runs on eSPI mode, from BMC side, the following VW messages are ++done in firmware:: ++ ++ 1. SLAVE_BOOT_LOAD_DONE / SLAVE_BOOT_LOAD_STATUS ++ 2. SUS_ACK ++ 3. OOB_RESET_ACK ++ 4. HOST_RESET_ACK ++ ++``eSPI Virtual Wires (VW)`` ++ +----------------------+---------+---------------------------------------+ ++ |Virtual Wire |PCH Pin |Comments | ++ | |Direction| | ++ +======================+=========+=======================================+ ++ |SUS_WARN# |Output |PCH pin is a GPIO when eSPI is enabled.| ++ | | |eSPI controller receives as VW message.| ++ +----------------------+---------+---------------------------------------+ ++ |SUS_ACK# |Input |PCH pin is a GPIO when eSPI is enabled.| ++ | | |eSPI controller receives as VW message.| ++ +----------------------+---------+---------------------------------------+ ++ |SLAVE_BOOT_LOAD_DONE |Input |Sent when the BMC has completed its | ++ | | |boot process as an indication to | ++ | | |eSPI-MC to continue with the G3 to S0 | ++ | | |exit. | ++ | | |The eSPI Master waits for the assertion| ++ | | |of this virtual wire before proceeding | ++ | | |with the SLP_S5# deassertion. | ++ | | |The intent is that it is never changed | ++ | | |except on a G3 exit - it is reset on a | ++ | | |G3 entry. | ++ +----------------------+---------+---------------------------------------+ ++ |SLAVE_BOOT_LOAD_STATUS|Input |Sent upon completion of the Slave Boot | ++ | | |Load from the attached flash. A stat of| ++ | | |1 indicates that the boot code load was| ++ | | |successful and that the integrity of | ++ | | |the image is intact. | ++ +----------------------+---------+---------------------------------------+ ++ |HOST_RESET_WARN |Output |Sent from the MC just before the Host | ++ | | |is about to enter reset. Upon receiving| ++ | | |, the BMC must flush and quiesce its | ++ | | |upstream Peripheral Channel request | ++ | | |queues and assert HOST_RESET_ACK VWire.| ++ | | |The MC subsequently completes any | ++ | | |outstanding posted transactions or | ++ | | |completions and then disables the | ++ | | |Peripheral Channel via a write to | ++ | | |the Slave's Configuration Register. | ++ +----------------------+---------+---------------------------------------+ ++ |HOST_RESET_ACK |Input |ACK for the HOST_RESET_WARN message | ++ +----------------------+---------+---------------------------------------+ ++ |OOB_RESET_WARN |Output |Sent from the MC just before the OOB | ++ | | |processor is about to enter reset. Upon| ++ | | |receiving, the BMC must flush and | ++ | | |quiesce its OOB Channel upstream | ++ | | |request queues and assert OOB_RESET_ACK| ++ | | |VWire. The-MC subsequently completes | ++ | | |any outstanding posted transactions or | ++ | | |completions and then disables the OOB | ++ | | |Channel via a write to the Slave's | ++ | | |Configuration Register. | ++ +----------------------+---------+---------------------------------------+ ++ |OOB_RESET_ACK |Input |ACK for OOB_RESET_WARN message | ++ +----------------------+---------+---------------------------------------+ ++ ++`Intel C620 Series Chipset Platform Controller Hub ++<https://www.intel.com/content/www/us/en/chipsets/c620-series-chipset-datasheet.html>`_ ++ ++ -- 17. Enhanced Serial Peripheral Interface ++ ++ ++`Enhanced Serial Peripheral Interface (eSPI) ++- Interface Base Specification (for Client and Server Platforms) ++<https://www.intel.com/content/dam/support/us/en/documents/software/chipset-software/327432-004_espi_base_specification_rev1.0.pdf>`_ ++ +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 4a302d745b09..165a2bddc6cd 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -266,6 +266,7 @@ + clocks = <&syscon ASPEED_CLK_APB>; + interrupt-controller; + #interrupt-cells = <2>; ++ status = "disabled"; + }; + + sgpio: sgpio@1e780200 { +@@ -360,6 +361,9 @@ + reg = <0x1e6ee000 0x100>; + interrupts = <23>; + status = "disabled"; ++ clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_espi_default>; + }; + + lpc: lpc@1e789000 { +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index fe1e2a4072a8..f2062546250c 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG + bus. System Configuration interface is one of the possible means + of generating transactions on this bus. + ++config ASPEED_ESPI_SLAVE ++ depends on ARCH_ASPEED || COMPILE_TEST ++ depends on REGMAP_MMIO ++ tristate "Aspeed ast2500 eSPI slave device driver" ++ ---help--- ++ Control Aspeed ast2500 eSPI slave controller to handle event ++ which needs the firmware's processing. ++ + config ASPEED_LPC_CTRL + depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON + tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index a2b85ec21d09..bb89694e6b4b 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -53,6 +53,7 @@ obj-$(CONFIG_GENWQE) += genwqe/ + obj-$(CONFIG_ECHO) += echo/ + obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o + obj-$(CONFIG_CXL_BASE) += cxl/ ++obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o + obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o + obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o + obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o +diff --git a/drivers/misc/aspeed-espi-slave.c b/drivers/misc/aspeed-espi-slave.c +new file mode 100644 +index 000000000000..36ae867ca6f9 +--- /dev/null ++++ b/drivers/misc/aspeed-espi-slave.c +@@ -0,0 +1,353 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2012-2015, ASPEED Technology Inc. ++ * Copyright (c) 2015-2018, Intel Corporation. ++ */ ++ ++#include <linux/atomic.h> ++#include <linux/clk.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/miscdevice.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/poll.h> ++#include <linux/regmap.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/timer.h> ++ ++#define DEVICE_NAME "aspeed-espi-slave" ++ ++#define ESPI_CTRL 0x00 ++#define ESPI_CTRL_SW_RESET GENMASK(31, 24) ++#define ESPI_CTRL_OOB_CHRDY BIT(4) ++#define ESPI_ISR 0x08 ++#define ESPI_ISR_HW_RESET BIT(31) ++#define ESPI_ISR_VW_SYS_EVT1 BIT(22) ++#define ESPI_ISR_VW_SYS_EVT BIT(8) ++#define ESPI_IER 0x0C ++#define ESPI_DATA_PORT 0x28 ++#define ESPI_DATA_PORT_ASPEED 0xa8 ++#define ESPI_SYS_IER 0x94 ++#define ESPI_SYS_EVENT 0x98 ++#define ESPI_SYS_INT_T0 0x110 ++#define ESPI_SYS_INT_T1 0x114 ++#define ESPI_SYS_INT_T2 0x118 ++#define ESPI_SYS_ISR 0x11C ++#define ESPI_SYSEVT_HOST_RST_ACK BIT(27) ++#define ESPI_SYSEVT_SLAVE_BOOT_STATUS BIT(23) ++#define ESPI_SYSEVT_SLAVE_BOOT_DONE BIT(20) ++#define ESPI_SYSEVT_OOB_RST_ACK BIT(16) ++#define ESPI_SYSEVT_HOST_RST_WARN BIT(8) ++#define ESPI_SYSEVT_OOB_RST_WARN BIT(6) ++#define ESPI_SYSEVT_PLT_RST_N BIT(5) ++#define ESPI_SYS1_IER 0x100 ++#define ESPI_SYS1_EVENT 0x104 ++#define ESPI_SYS1_INT_T0 0x120 ++#define ESPI_SYS1_INT_T1 0x124 ++#define ESPI_SYS1_INT_T2 0x128 ++#define ESPI_SYS1_ISR 0x12C ++#define ESPI_SYSEVT1_SUS_ACK BIT(20) ++#define ESPI_SYSEVT1_SUS_WARN BIT(0) ++ ++struct aspeed_espi_slave_data { ++ struct regmap *map; ++ struct clk *clk; ++}; ++ ++static void aspeed_espi_slave_sys_event(struct platform_device *pdev) ++{ ++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ u32 sts, evt; ++ ++ if (regmap_read(priv->map, ESPI_SYS_ISR, &sts) != 0 || ++ regmap_read(priv->map, ESPI_SYS_EVENT, &evt) != 0) { ++ dev_err(dev, "regmap_read failed\n"); ++ return; ++ } ++ ++ dev_dbg(dev, "sys: sts = %08x, evt = %08x\n", sts, evt); ++ ++ if ((evt & ESPI_SYSEVT_SLAVE_BOOT_STATUS) == 0) { ++ dev_info(dev, "Setting espi slave boot done\n"); ++ regmap_write(priv->map, ESPI_SYS_EVENT, ++ evt | ESPI_SYSEVT_SLAVE_BOOT_STATUS | ++ ESPI_SYSEVT_SLAVE_BOOT_DONE); ++ } ++#if 0 ++ if (sts & ESPI_SYSEVT_HOST_RST_WARN) { ++ dev_info(dev, "ESPI_SYSEVT_HOST_RST_WARN; %s ack\n", ++ (evt & ESPI_SYSEVT_HOST_RST_WARN ? "send" : "clr")); ++ regmap_write_bits(priv->map, ESPI_SYS_EVENT, ++ ESPI_SYSEVT_HOST_RST_ACK, ++ evt & ESPI_SYSEVT_HOST_RST_WARN ? ++ ESPI_SYSEVT_HOST_RST_ACK : 0); ++ } ++ if (sts & ESPI_SYSEVT_OOB_RST_WARN) { ++ dev_info(dev, "ESPI_SYSEVT_OOB_RST_WARN; %s ack\n", ++ (evt & ESPI_SYSEVT_OOB_RST_WARN ? "send" : "clr")); ++ regmap_write_bits(priv->map, ESPI_SYS_EVENT, ++ ESPI_SYSEVT_OOB_RST_ACK, ++ evt & ESPI_SYSEVT_OOB_RST_WARN ? ++ ESPI_SYSEVT_OOB_RST_ACK : 0); ++ } ++#else ++ if (sts & ESPI_SYSEVT_HOST_RST_WARN) { ++ if (evt & ESPI_SYSEVT_HOST_RST_WARN) { ++ dev_info(dev, "ESPI_SYSEVT_HOST_RST_WARN; send ack\n"); ++ regmap_write_bits(priv->map, ESPI_SYS_EVENT, ++ ESPI_SYSEVT_HOST_RST_ACK, ESPI_SYSEVT_HOST_RST_ACK); ++ } ++ } ++ if (sts & ESPI_SYSEVT_OOB_RST_WARN) { ++ if (evt & ESPI_SYSEVT_OOB_RST_WARN) { ++ dev_info(dev, "ESPI_SYSEVT_OOB_RST_WARN; send ack\n"); ++ regmap_write_bits(priv->map, ESPI_SYS_EVENT, ++ ESPI_SYSEVT_OOB_RST_ACK, ESPI_SYSEVT_OOB_RST_ACK); ++ } ++ } ++#endif ++ regmap_write(priv->map, ESPI_SYS_ISR, sts); ++} ++ ++static void aspeed_espi_slave_sys1_event(struct platform_device *pdev) ++{ ++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ u32 sts, evt; ++ ++ if (regmap_read(priv->map, ESPI_SYS1_ISR, &sts) != 0 || ++ regmap_read(priv->map, ESPI_SYS1_EVENT, &evt) != 0) { ++ dev_err(dev, "regmap_read failed\n"); ++ return; ++ } ++ dev_dbg(dev, "sys1: sts = %08x, evt = %08x\n", sts, evt); ++ ++#if 0 ++ if (sts & ESPI_SYSEVT1_SUS_WARN) { ++ dev_info(dev, "ESPI_SYSEVT1_SUS_WARN; %s ack\n", ++ (evt & ESPI_SYSEVT1_SUS_WARN ? "send" : "clr")); ++ regmap_write_bits(priv->map, ESPI_SYS1_EVENT, ++ ESPI_SYSEVT1_SUS_ACK, ++ evt & ESPI_SYSEVT1_SUS_WARN ? ++ ESPI_SYSEVT1_SUS_ACK : 0); ++ } ++#else ++ if (sts & ESPI_SYSEVT1_SUS_WARN) { ++ if (evt & ESPI_SYSEVT1_SUS_WARN) { ++ dev_info(dev, "ESPI_SYSEVT_OOB_RST_WARN; send ack\n"); ++ regmap_write_bits(priv->map, ESPI_SYS1_EVENT, ++ ESPI_SYSEVT1_SUS_ACK, ESPI_SYSEVT1_SUS_ACK); ++ } ++ } ++#endif ++ regmap_write(priv->map, ESPI_SYS1_ISR, sts); ++} ++ ++static void aspeed_espi_slave_boot_ack(struct platform_device *pdev) ++{ ++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ u32 evt; ++ ++ if (regmap_read(priv->map, ESPI_SYS_EVENT, &evt) == 0 && ++ (evt & ESPI_SYSEVT_SLAVE_BOOT_STATUS) == 0) { ++ dev_info(dev, "Setting espi slave boot done\n"); ++ regmap_write(priv->map, ESPI_SYS_EVENT, ++ evt | ESPI_SYSEVT_SLAVE_BOOT_STATUS | ++ ESPI_SYSEVT_SLAVE_BOOT_DONE); ++ } ++ ++ if (regmap_read(priv->map, ESPI_SYS1_EVENT, &evt) == 0 && ++ (evt & ESPI_SYSEVT1_SUS_WARN) != 0 && ++ (evt & ESPI_SYSEVT1_SUS_ACK) == 0) { ++ dev_info(dev, "Boot SUS WARN set; send ack\n"); ++ regmap_write(priv->map, ESPI_SYS1_EVENT, ++ evt | ESPI_SYSEVT1_SUS_ACK); ++ } ++} ++ ++static irqreturn_t aspeed_espi_slave_irq(int irq, void *arg) ++{ ++ struct platform_device *pdev = arg; ++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ u32 sts; ++ ++ if (regmap_read(priv->map, ESPI_ISR, &sts) != 0) { ++ dev_err(dev, "regmap_read failed\n"); ++ return IRQ_NONE; ++ } ++ ++ dev_dbg(dev, "ESPI_ISR: %08x\n", sts); ++ ++ if (sts & ESPI_ISR_VW_SYS_EVT) ++ aspeed_espi_slave_sys_event(pdev); ++ ++ if (sts & ESPI_ISR_VW_SYS_EVT1) ++ aspeed_espi_slave_sys1_event(pdev); ++ ++ /* ++ if (sts & ESPI_ISR_HW_RESET) { ++ regmap_write_bits(priv->map, ESPI_CTRL, ++ ESPI_CTRL_SW_RESET, 0); ++ regmap_write_bits(priv->map, ESPI_CTRL, ++ ESPI_CTRL_SW_RESET, ESPI_CTRL_SW_RESET); ++ ++ aspeed_espi_slave_boot_ack(pdev); ++ } ++ */ ++ ++ regmap_write(priv->map, ESPI_ISR, sts); ++ ++ return IRQ_HANDLED; ++} ++ ++/* Setup Interrupt Type/Enable of System Event from Master ++ * T2 T1 T0 ++ * 1). HOST_RST_WARN : Dual Edge 1 0 0 ++ * 2). OOB_RST_WARN : Dual Edge 1 0 0 ++ * 3). PLTRST_N : Dual Edge 1 0 0 ++ */ ++#define ESPI_SYS_INT_T0_SET 0x00000000 ++#define ESPI_SYS_INT_T1_SET 0x00000000 ++#define ESPI_SYS_INT_T2_SET \ ++(ESPI_SYSEVT_HOST_RST_WARN | ESPI_SYSEVT_OOB_RST_WARN | ESPI_SYSEVT_PLT_RST_N) ++#define ESPI_SYS_INT_SET \ ++(ESPI_SYSEVT_HOST_RST_WARN | ESPI_SYSEVT_OOB_RST_WARN | ESPI_SYSEVT_PLT_RST_N) ++ ++/* Setup Interrupt Type/Enable of System Event 1 from Master ++ * T2 T1 T0 ++ * 1). SUS_WARN : Rising Edge 0 0 1 ++ */ ++#define ESPI_SYS1_INT_T0_SET ESPI_SYSEVT1_SUS_WARN ++#define ESPI_SYS1_INT_T1_SET 0x00000000 ++#define ESPI_SYS1_INT_T2_SET 0x00000000 ++#define ESPI_SYS1_INT_SET ESPI_SYSEVT1_SUS_WARN ++ ++static int aspeed_espi_slave_config_irq(struct platform_device *pdev) ++{ ++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ int irq; ++ int rc; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ regmap_write_bits(priv->map, ESPI_CTRL, ESPI_CTRL_OOB_CHRDY, ++ ESPI_CTRL_OOB_CHRDY); ++ ++ regmap_write(priv->map, ESPI_SYS_INT_T0, ESPI_SYS_INT_T0_SET); ++ regmap_write(priv->map, ESPI_SYS_INT_T1, ESPI_SYS_INT_T1_SET); ++ regmap_write(priv->map, ESPI_SYS_INT_T2, ESPI_SYS_INT_T2_SET); ++ regmap_write(priv->map, ESPI_SYS_IER, ESPI_SYS_INT_SET); ++ ++ regmap_write(priv->map, ESPI_SYS1_INT_T0, ESPI_SYS1_INT_T0_SET); ++ regmap_write(priv->map, ESPI_SYS1_INT_T1, ESPI_SYS1_INT_T1_SET); ++ regmap_write(priv->map, ESPI_SYS1_INT_T2, ESPI_SYS1_INT_T2_SET); ++ regmap_write(priv->map, ESPI_SYS1_IER, ESPI_SYS1_INT_SET); ++ ++ regmap_write(priv->map, ESPI_IER, 0xFFFFFFFF); ++ ++ aspeed_espi_slave_boot_ack(pdev); ++ ++ rc = devm_request_irq(dev, irq, aspeed_espi_slave_irq, IRQF_SHARED, ++ dev_name(dev), pdev); ++ if (rc < 0) ++ return rc; ++ ++ return 0; ++} ++ ++static const struct regmap_config espi_slave_regmap_cfg = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = ESPI_SYS1_ISR, ++}; ++ ++static int aspeed_espi_slave_probe(struct platform_device *pdev) ++{ ++ struct aspeed_espi_slave_data *priv; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ void __iomem *regs; ++ int rc; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->map = devm_regmap_init_mmio(dev, regs, &espi_slave_regmap_cfg); ++ if (IS_ERR(priv->map)) ++ return PTR_ERR(priv->map); ++ ++ priv->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(priv->clk)) { ++ dev_err(dev, "couldn't get clock\n"); ++ return PTR_ERR(priv->clk); ++ } ++ rc = clk_prepare_enable(priv->clk); ++ if (rc) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return rc; ++ } ++ ++ dev_set_name(dev, DEVICE_NAME); ++ ++ platform_set_drvdata(pdev, priv); ++ ++ rc = aspeed_espi_slave_config_irq(pdev); ++ if (rc) { ++ platform_set_drvdata(pdev, NULL); ++ goto err; ++ } ++ ++ dev_info(dev, "aspeed,ast2500-espi-slave probe complete\n"); ++ return 0; ++ ++err: ++ clk_disable_unprepare(priv->clk); ++ return rc; ++} ++ ++ ++static int aspeed_espi_slave_remove(struct platform_device *pdev) ++{ ++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev); ++ ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id of_espi_slave_match_table[] = { ++ { .compatible = "aspeed,ast2500-espi-slave" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, of_espi_slave_match_table); ++ ++static struct platform_driver aspeed_espi_slave_driver = { ++ .driver = { ++ .name = DEVICE_NAME, ++ .of_match_table = of_match_ptr(of_espi_slave_match_table), ++ }, ++ .probe = aspeed_espi_slave_probe, ++ .remove = aspeed_espi_slave_remove, ++}; ++module_platform_driver(aspeed_espi_slave_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); ++MODULE_DESCRIPTION("Linux device interface to the eSPI slave"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0025-dts-add-AST2500-LPC-SIO-tree-node.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0025-dts-add-AST2500-LPC-SIO-tree-node.patch new file mode 100644 index 000000000..73bd68f21 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0025-dts-add-AST2500-LPC-SIO-tree-node.patch @@ -0,0 +1,32 @@ +From ba357b37e1041b6fe0e5012cf09571381207aa9b Mon Sep 17 00:00:00 2001 +From: Haiyue Wang <haiyue.wang@linux.intel.com> +Date: Sat, 24 Feb 2018 11:23:46 +0800 +Subject: [PATCH] dts: add AST2500 LPC SIO tree node + +Add the AST2500 LPC SIO tree node. + +Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com> +--- + arch/arm/boot/dts/aspeed-g5.dtsi | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index baf230034480..f7e812d36641 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -464,6 +464,12 @@ + compatible = "aspeed,bmc-misc"; + }; + ++ lpc_sio: lpc-sio@100 { ++ compatible = "aspeed,ast2500-lpc-sio"; ++ reg = <0x100 0x20>; ++ status = "disabled"; ++ }; ++ + mbox: mbox@180 { + compatible = "aspeed,ast2500-mbox"; + reg = <0x180 0x5c>; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch new file mode 100644 index 000000000..3ba6fd56e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch @@ -0,0 +1,392 @@ +From e734cd91f288838a491a75e16f3a09d353079139 Mon Sep 17 00:00:00 2001 +From: "Jason M. Bills" <jason.m.bills@intel.com> +Date: Wed, 4 Apr 2018 13:52:39 -0700 +Subject: [PATCH] Add support for new PECI commands + +Signed-off-by: Jason M. Bills <jason.m.bills@intel.com> +--- + drivers/peci/peci-core.c | 200 ++++++++++++++++++++++++++++++++++++++++ + include/uapi/linux/peci-ioctl.h | 109 ++++++++++++++++++++++ + 2 files changed, 309 insertions(+) + +diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c +index fac8c72dcda8..62dada99afee 100644 +--- a/drivers/peci/peci-core.c ++++ b/drivers/peci/peci-core.c +@@ -242,6 +242,9 @@ static int peci_scan_cmd_mask(struct peci_adapter *adapter) + adapter->cmd_mask |= BIT(PECI_CMD_GET_TEMP); + adapter->cmd_mask |= BIT(PECI_CMD_GET_DIB); + adapter->cmd_mask |= BIT(PECI_CMD_PING); ++ adapter->cmd_mask |= BIT(PECI_CMD_RD_END_PT_CFG); ++ adapter->cmd_mask |= BIT(PECI_CMD_CRASHDUMP_DISC); ++ adapter->cmd_mask |= BIT(PECI_CMD_CRASHDUMP_GET_FRAME); + + return rc; + } +@@ -515,6 +518,197 @@ static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) + return rc; + } + ++static int peci_ioctl_rd_end_pt_cfg(struct peci_adapter *adapter, void *vmsg) ++{ ++ struct peci_rd_end_pt_cfg_msg *umsg = vmsg; ++ struct peci_xfer_msg msg; ++ u32 address; ++ int rc = 0; ++ ++ switch (umsg->msg_type) { ++ case RDENDPTCFG_TYPE_LOCAL_PCI: ++ case RDENDPTCFG_TYPE_PCI: ++ /** ++ * Per the PECI spec, the read length must be a byte, word, ++ * or dword ++ */ ++ if (umsg->rx_len != 1 && umsg->rx_len != 2 && ++ umsg->rx_len != 4) { ++ dev_dbg(&adapter->dev, ++ "Invalid read length, rx_len: %d\n", ++ umsg->rx_len); ++ return -EINVAL; ++ } ++ ++ address = umsg->params.pci_cfg.reg; /* [11:0] - Register */ ++ address |= (u32)umsg->params.pci_cfg.function ++ << 12; /* [14:12] - Function */ ++ address |= (u32)umsg->params.pci_cfg.device ++ << 15; /* [19:15] - Device */ ++ address |= (u32)umsg->params.pci_cfg.bus ++ << 20; /* [27:20] - Bus */ ++ /* [31:28] - Reserved */ ++ msg.addr = umsg->addr; ++ msg.tx_len = RDENDPTCFG_PCI_WRITE_LEN; ++ msg.rx_len = RDENDPTCFG_READ_LEN_BASE + umsg->rx_len; ++ msg.tx_buf[0] = RDENDPTCFG_PECI_CMD; ++ msg.tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */ ++ msg.tx_buf[2] = umsg->msg_type; /* Message Type */ ++ msg.tx_buf[3] = 0x00; /* Endpoint ID */ ++ msg.tx_buf[4] = 0x00; /* Reserved */ ++ msg.tx_buf[5] = 0x00; /* Reserved */ ++ msg.tx_buf[6] = RDENDPTCFG_ADDR_TYPE_PCI; /* Address Type */ ++ msg.tx_buf[7] = umsg->params.pci_cfg.seg; /* PCI Segment */ ++ msg.tx_buf[8] = (u8)address; /* LSB - PCI Config Address */ ++ msg.tx_buf[9] = (u8)(address >> 8); /* PCI Config Address */ ++ msg.tx_buf[10] = (u8)(address >> 16); /* PCI Config Address */ ++ msg.tx_buf[11] = ++ (u8)(address >> 24); /* MSB - PCI Config Address */ ++ break; ++ case RDENDPTCFG_TYPE_MMIO: ++ /** ++ * Per the PECI spec, the read length must be a byte, word, ++ * dword, or qword ++ */ ++ if (umsg->rx_len != 1 && umsg->rx_len != 2 && ++ umsg->rx_len != 4 && umsg->rx_len != 8) { ++ dev_dbg(&adapter->dev, ++ "Invalid read length, rx_len: %d\n", ++ umsg->rx_len); ++ return -EINVAL; ++ } ++ /** ++ * Per the PECI spec, the address type must specify either DWORD ++ * or QWORD ++ */ ++ if (umsg->params.mmio.addr_type != ++ RDENDPTCFG_ADDR_TYPE_MMIO_D && ++ umsg->params.mmio.addr_type != ++ RDENDPTCFG_ADDR_TYPE_MMIO_Q) { ++ dev_dbg(&adapter->dev, ++ "Invalid address type, addr_type: %d\n", ++ umsg->params.mmio.addr_type); ++ return -EINVAL; ++ } ++ ++ address = umsg->params.mmio.function; /* [2:0] - Function */ ++ address |= (u32)umsg->params.mmio.device ++ << 3; /* [7:3] - Device */ ++ ++ msg.addr = umsg->addr; ++ msg.tx_len = RDENDPTCFG_MMIO_D_WRITE_LEN; ++ msg.rx_len = RDENDPTCFG_READ_LEN_BASE + umsg->rx_len; ++ msg.tx_buf[0] = RDENDPTCFG_PECI_CMD; ++ msg.tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */ ++ msg.tx_buf[2] = umsg->msg_type; /* Message Type */ ++ msg.tx_buf[3] = 0x00; /* Endpoint ID */ ++ msg.tx_buf[4] = 0x00; /* Reserved */ ++ msg.tx_buf[5] = umsg->params.mmio.bar; /* BAR # */ ++ msg.tx_buf[6] = umsg->params.mmio.addr_type; /* Address Type */ ++ msg.tx_buf[7] = umsg->params.mmio.seg; /* PCI Segment */ ++ msg.tx_buf[8] = (u8)address; /* Function/Device */ ++ msg.tx_buf[9] = umsg->params.mmio.bus; /* PCI Bus */ ++ msg.tx_buf[10] = (u8)umsg->params.mmio ++ .offset; /* LSB - Register Offset */ ++ msg.tx_buf[11] = (u8)(umsg->params.mmio.offset ++ >> 8); /* Register Offset */ ++ msg.tx_buf[12] = (u8)(umsg->params.mmio.offset ++ >> 16); /* Register Offset */ ++ msg.tx_buf[13] = (u8)(umsg->params.mmio.offset ++ >> 24); /* MSB - DWORD Register Offset */ ++ if (umsg->params.mmio.addr_type ++ == RDENDPTCFG_ADDR_TYPE_MMIO_Q) { ++ msg.tx_len = RDENDPTCFG_MMIO_Q_WRITE_LEN; ++ msg.tx_buf[14] = (u8)(umsg->params.mmio.offset ++ >> 32); /* Register Offset */ ++ msg.tx_buf[15] = (u8)(umsg->params.mmio.offset ++ >> 40); /* Register Offset */ ++ msg.tx_buf[16] = (u8)(umsg->params.mmio.offset ++ >> 48); /* Register Offset */ ++ msg.tx_buf[17] = ++ (u8)(umsg->params.mmio.offset ++ >> 56); /* MSB - QWORD Register Offset */ ++ } ++ break; ++ } ++ ++ rc = peci_xfer_with_retries(adapter, &msg, false); ++ if (!rc) ++ memcpy(umsg->data, &msg.rx_buf[1], umsg->rx_len); ++ ++ return rc; ++} ++ ++static int peci_ioctl_crashdump_disc(struct peci_adapter *adapter, void *vmsg) ++{ ++ struct peci_crashdump_disc_msg *umsg = vmsg; ++ struct peci_xfer_msg msg; ++ int rc = 0; ++ ++ /* Per the EDS, the read length must be a byte, word, or qword */ ++ if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 8) { ++ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n", ++ umsg->rx_len); ++ return -EINVAL; ++ } ++ ++ msg.addr = umsg->addr; ++ msg.tx_len = CRASHDUMP_DISC_WRITE_LEN; ++ msg.rx_len = CRASHDUMP_DISC_READ_LEN_BASE + umsg->rx_len; ++ msg.tx_buf[0] = CRASHDUMP_CMD; ++ msg.tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */ ++ /* Host ID is 0 for PECI 3.0 */ ++ msg.tx_buf[2] = CRASHDUMP_DISC_VERSION; ++ msg.tx_buf[3] = CRASHDUMP_DISC_OPCODE; ++ msg.tx_buf[4] = umsg->subopcode; ++ msg.tx_buf[5] = umsg->param0; ++ msg.tx_buf[6] = (u8)umsg->param1; ++ msg.tx_buf[7] = (u8)(umsg->param1 >> 8); ++ msg.tx_buf[8] = umsg->param2; ++ ++ rc = peci_xfer_with_retries(adapter, &msg, false); ++ if (!rc) ++ memcpy(umsg->data, &msg.rx_buf[1], umsg->rx_len); ++ ++ return rc; ++} ++ ++static int peci_ioctl_crashdump_get_frame(struct peci_adapter *adapter, ++ void *vmsg) ++{ ++ struct peci_crashdump_get_frame_msg *umsg = vmsg; ++ struct peci_xfer_msg msg; ++ int rc = 0; ++ ++ /* Per the EDS, the read length must be a qword or dqword */ ++ if (umsg->rx_len != 8 && umsg->rx_len != 16) { ++ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n", ++ umsg->rx_len); ++ return -EINVAL; ++ } ++ ++ msg.addr = umsg->addr; ++ msg.tx_len = CRASHDUMP_GET_FRAME_WRITE_LEN; ++ msg.rx_len = CRASHDUMP_GET_FRAME_READ_LEN_BASE + umsg->rx_len; ++ msg.tx_buf[0] = CRASHDUMP_CMD; ++ msg.tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */ ++ /* Host ID is 0 for PECI 3.0 */ ++ msg.tx_buf[2] = CRASHDUMP_GET_FRAME_VERSION; ++ msg.tx_buf[3] = CRASHDUMP_GET_FRAME_OPCODE; ++ msg.tx_buf[4] = (u8)umsg->param0; ++ msg.tx_buf[5] = (u8)(umsg->param0 >> 8); ++ msg.tx_buf[6] = (u8)umsg->param1; ++ msg.tx_buf[7] = (u8)(umsg->param1 >> 8); ++ msg.tx_buf[8] = (u8)umsg->param2; ++ msg.tx_buf[8] = (u8)(umsg->param2 >> 8); ++ ++ rc = peci_xfer_with_retries(adapter, &msg, false); ++ if (!rc) ++ memcpy(umsg->data, &msg.rx_buf[1], umsg->rx_len); ++ ++ return rc; ++} ++ + typedef int (*peci_ioctl_fn_type)(struct peci_adapter *, void *); + + static const peci_ioctl_fn_type peci_ioctl_fn[PECI_CMD_MAX] = { +@@ -530,6 +724,9 @@ static const peci_ioctl_fn_type peci_ioctl_fn[PECI_CMD_MAX] = { + NULL, /* Reserved */ + peci_ioctl_rd_pci_cfg_local, + peci_ioctl_wr_pci_cfg_local, ++ peci_ioctl_rd_end_pt_cfg, ++ peci_ioctl_crashdump_disc, ++ peci_ioctl_crashdump_get_frame, + }; + + /** +@@ -592,6 +789,9 @@ static long peci_ioctl(struct file *file, unsigned int iocmd, unsigned long arg) + case PECI_IOC_RD_PCI_CFG: + case PECI_IOC_RD_PCI_CFG_LOCAL: + case PECI_IOC_WR_PCI_CFG_LOCAL: ++ case PECI_IOC_RD_END_PT_CFG: ++ case PECI_IOC_CRASHDUMP_DISC: ++ case PECI_IOC_CRASHDUMP_GET_FRAME: + cmd = _IOC_NR(iocmd); + msg_len = _IOC_SIZE(iocmd); + break; +diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h +index a6dae71cbff5..5040d1cb4a3d 100644 +--- a/include/uapi/linux/peci-ioctl.h ++++ b/include/uapi/linux/peci-ioctl.h +@@ -31,6 +31,40 @@ + #define PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */ + #define PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */ + ++/* RdEndPointCfg Parameters */ ++enum rdendptcfg_msg_type { ++ RDENDPTCFG_TYPE_LOCAL_PCI = 0x03, ++ RDENDPTCFG_TYPE_PCI = 0x04, ++ RDENDPTCFG_TYPE_MMIO = 0x05, ++}; ++ ++enum rdendptcfg_addr_type { ++ RDENDPTCFG_ADDR_TYPE_PCI = 0x04, ++ RDENDPTCFG_ADDR_TYPE_MMIO_D = 0x05, ++ RDENDPTCFG_ADDR_TYPE_MMIO_Q = 0x06, ++}; ++ ++/* Crashdump Parameters */ ++enum crashdump_agent { ++ CRASHDUMP_CORE = 0x00, ++ CRASHDUMP_TOR = 0x01, ++}; ++ ++enum crashdump_discovery_sub_opcode { ++ CRASHDUMP_ENABLED = 0x00, ++ CRASHDUMP_NUM_AGENTS = 0x01, ++ CRASHDUMP_AGENT_DATA = 0x02, ++}; ++ ++enum crashdump_agent_data_param { ++ CRASHDUMP_AGENT_ID = 0x00, ++ CRASHDUMP_AGENT_PARAM = 0x01, ++}; ++ ++enum crashdump_agent_param { ++ CRASHDUMP_PAYLOAD_SIZE = 0x00, ++}; ++ + /* RdPkgConfig Index */ + #define MBX_INDEX_CPU_ID 0 /* Package Identifier Read */ + #define MBX_INDEX_VR_DEBUG 1 /* VR Debug */ +@@ -136,6 +170,22 @@ + #define WRPCICFGLOCAL_READ_LEN 1 + #define WRPCICFGLOCAL_PECI_CMD 0xe5 + ++#define RDENDPTCFG_PCI_WRITE_LEN 0x0C ++#define RDENDPTCFG_MMIO_D_WRITE_LEN 0x0E ++#define RDENDPTCFG_MMIO_Q_WRITE_LEN 0x12 ++#define RDENDPTCFG_READ_LEN_BASE 1 ++#define RDENDPTCFG_PECI_CMD 0xC1 ++ ++#define CRASHDUMP_DISC_WRITE_LEN 9 ++#define CRASHDUMP_DISC_READ_LEN_BASE 1 ++#define CRASHDUMP_DISC_VERSION 1 ++#define CRASHDUMP_DISC_OPCODE 1 ++#define CRASHDUMP_GET_FRAME_WRITE_LEN 10 ++#define CRASHDUMP_GET_FRAME_READ_LEN_BASE 1 ++#define CRASHDUMP_GET_FRAME_VERSION 3 ++#define CRASHDUMP_GET_FRAME_OPCODE 3 ++#define CRASHDUMP_CMD 0x71 ++ + #define PECI_BUFFER_SIZE 32 + + /** +@@ -172,6 +222,9 @@ enum peci_cmd { + PECI_CMD_WR_PCI_CFG, + PECI_CMD_RD_PCI_CFG_LOCAL, + PECI_CMD_WR_PCI_CFG_LOCAL, ++ PECI_CMD_RD_END_PT_CFG, ++ PECI_CMD_CRASHDUMP_DISC, ++ PECI_CMD_CRASHDUMP_GET_FRAME, + PECI_CMD_MAX + }; + +@@ -366,6 +419,50 @@ struct peci_wr_pci_cfg_local_msg { + __u32 value; + } __attribute__((__packed__)); + ++struct peci_rd_end_pt_cfg_msg { ++ __u8 addr; ++ __u8 msg_type; ++ union { ++ struct { ++ __u8 seg; ++ __u8 bus; ++ __u8 device; ++ __u8 function; ++ __u16 reg; ++ } pci_cfg; ++ struct { ++ __u8 seg; ++ __u8 bus; ++ __u8 device; ++ __u8 function; ++ __u8 bar; ++ __u8 addr_type; ++ __u64 offset; ++ } mmio; ++ } params; ++ __u8 rx_len; ++ __u8 data[8]; ++} __attribute__((__packed__)); ++ ++struct peci_crashdump_disc_msg { ++ __u8 addr; ++ __u8 subopcode; ++ __u8 param0; ++ __u16 param1; ++ __u8 param2; ++ __u8 rx_len; ++ __u8 data[8]; ++} __attribute__((__packed__)); ++ ++struct peci_crashdump_get_frame_msg { ++ __u8 addr; ++ __u16 param0; ++ __u16 param1; ++ __u16 param2; ++ __u8 rx_len; ++ __u8 data[16]; ++} __attribute__((__packed__)); ++ + #define PECI_IOC_BASE 0xb7 + + #define PECI_IOC_XFER \ +@@ -400,4 +497,16 @@ struct peci_wr_pci_cfg_local_msg { + _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG_LOCAL, \ + struct peci_wr_pci_cfg_local_msg) + ++#define PECI_IOC_RD_END_PT_CFG \ ++ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_END_PT_CFG, \ ++ struct peci_rd_end_pt_cfg_msg) ++ ++#define PECI_IOC_CRASHDUMP_DISC \ ++ _IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_DISC, \ ++ struct peci_crashdump_disc_msg) ++ ++#define PECI_IOC_CRASHDUMP_GET_FRAME \ ++ _IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_GET_FRAME, \ ++ struct peci_crashdump_get_frame_msg) ++ + #endif /* __PECI_IOCTL_H */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch new file mode 100644 index 000000000..860a1ba5d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch @@ -0,0 +1,1138 @@ +From 43470f186979483ba6c1e6374c7ea3a129622862 Mon Sep 17 00:00:00 2001 +From: "Hunt, Bryan" <bryan.hunt@intel.com> +Date: Fri, 30 Mar 2018 10:48:01 -0700 +Subject: [PATCH] Add AST2500d JTAG driver + +Adding aspeed jtag driver + +Signed-off-by: Hunt, Bryan <bryan.hunt@intel.com> +--- + arch/arm/boot/dts/aspeed-g5.dtsi | 9 + + drivers/Kconfig | 1 + + drivers/Makefile | 1 + + drivers/jtag/Kconfig | 13 + + drivers/jtag/Makefile | 1 + + drivers/jtag/jtag_aspeed.c | 963 +++++++++++++++++++++++++++++++++++++++ + include/uapi/linux/jtag_drv.h | 73 +++ + 7 files changed, 1061 insertions(+) + create mode 100644 drivers/jtag/Kconfig + create mode 100644 drivers/jtag/Makefile + create mode 100644 drivers/jtag/jtag_aspeed.c + create mode 100644 include/uapi/linux/jtag_drv.h + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index a7bbc2adecc9..b63003c2c0c7 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -366,6 +366,15 @@ + pinctrl-0 = <&pinctrl_espi_default>; + }; + ++ jtag: jtag@1e6e4000 { ++ compatible = "aspeed,ast2500-jtag"; ++ reg = <0x1e6e2004 0x4 0x1e6e4000 0x1c>; ++ clocks = <&syscon ASPEED_CLK_APB>; ++ resets = <&syscon ASPEED_RESET_JTAG_MASTER>; ++ interrupts = <43>; ++ status = "disabled"; ++ }; ++ + lpc: lpc@1e789000 { + compatible = "aspeed,ast2500-lpc", "simple-mfd"; + reg = <0x1e789000 0x1000>; +diff --git a/drivers/Kconfig b/drivers/Kconfig +index c633db2b41fb..2778a5c33ca5 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -221,4 +221,5 @@ source "drivers/slimbus/Kconfig" + + source "drivers/peci/Kconfig" + ++source "drivers/jtag/Kconfig" + endmenu +diff --git a/drivers/Makefile b/drivers/Makefile +index 63c9b425e6e1..714067945fd2 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -187,3 +187,4 @@ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ + obj-$(CONFIG_SIOX) += siox/ + obj-$(CONFIG_GNSS) += gnss/ + obj-$(CONFIG_PECI) += peci/ ++obj-$(CONFIG_JTAG_ASPEED) += jtag/ +diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig +new file mode 100644 +index 000000000000..2e5d0a5bea90 +--- /dev/null ++++ b/drivers/jtag/Kconfig +@@ -0,0 +1,13 @@ ++menuconfig JTAG_ASPEED ++ tristate "ASPEED SoC JTAG controller support" ++ depends on HAS_IOMEM ++ depends on ARCH_ASPEED || COMPILE_TEST ++ help ++ This provides a support for ASPEED JTAG device, equipped on ++ ASPEED SoC 24xx and 25xx families. Drivers allows programming ++ of hardware devices, connected to SoC through the JTAG interface. ++ ++ If you want this support, you should say Y here. ++ ++ To compile this driver as a module, choose M here: the module will ++ be called jtag_aspeed. +diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile +new file mode 100644 +index 000000000000..db9b660e9f90 +--- /dev/null ++++ b/drivers/jtag/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_JTAG_ASPEED) += jtag_aspeed.o +diff --git a/drivers/jtag/jtag_aspeed.c b/drivers/jtag/jtag_aspeed.c +new file mode 100644 +index 000000000000..42e2a131873c +--- /dev/null ++++ b/drivers/jtag/jtag_aspeed.c +@@ -0,0 +1,963 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2012-2017 ASPEED Technology Inc. ++// Copyright (c) 2018 Intel Corporation ++ ++#include <linux/bitfield.h> ++#include <linux/delay.h> ++#include <linux/fs.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/jtag_drv.h> ++#include <linux/miscdevice.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++ ++#define SCU_RESET_JTAG BIT(22) ++ ++#define AST_JTAG_DATA 0x00 ++#define AST_JTAG_INST 0x04 ++#define AST_JTAG_CTRL 0x08 ++#define AST_JTAG_ISR 0x0C ++#define AST_JTAG_SW 0x10 ++#define AST_JTAG_TCK 0x14 ++#define AST_JTAG_IDLE 0x18 ++ ++/* AST_JTAG_CTRL - 0x08 : Engine Control */ ++#define JTAG_ENG_EN BIT(31) ++#define JTAG_ENG_OUT_EN BIT(30) ++#define JTAG_ENGINE_EN (JTAG_ENG_EN | JTAG_ENG_OUT_EN) ++#define JTAG_FORCE_TMS BIT(29) ++ ++#define JTAG_IR_UPDATE BIT(26) /* AST2500 only */ ++#define JTAG_INST_LEN_MASK GENMASK(25, 20) ++#define JTAG_LAST_INST BIT(17) ++#define JTAG_INST_EN BIT(16) ++#define JTAG_DATA_LEN_MASK GENMASK(9, 4) ++ ++#define JTAG_DR_UPDATE BIT(10) /* AST2500 only */ ++#define JTAG_LAST_DATA BIT(1) ++#define JTAG_DATA_EN BIT(0) ++ ++/* AST_JTAG_ISR - 0x0C : Interrupt status and enable */ ++#define JTAG_INST_PAUSE BIT(19) ++#define JTAG_INST_COMPLETE BIT(18) ++#define JTAG_DATA_PAUSE BIT(17) ++#define JTAG_DATA_COMPLETE BIT(16) ++ ++#define JTAG_INST_PAUSE_EN BIT(3) ++#define JTAG_INST_COMPLETE_EN BIT(2) ++#define JTAG_DATA_PAUSE_EN BIT(1) ++#define JTAG_DATA_COMPLETE_EN BIT(0) ++ ++/* AST_JTAG_SW - 0x10 : Software Mode and Status */ ++#define JTAG_SW_MODE_EN BIT(19) ++#define JTAG_SW_MODE_TCK BIT(18) ++#define JTAG_SW_MODE_TMS BIT(17) ++#define JTAG_SW_MODE_TDIO BIT(16) ++ ++/* AST_JTAG_TCK - 0x14 : TCK Control */ ++#define JTAG_TCK_DIVISOR_MASK GENMASK(10, 0) ++ ++/* #define USE_INTERRUPTS */ ++#define AST_JTAG_NAME "jtag" ++ ++static DEFINE_SPINLOCK(jtag_state_lock); ++ ++struct ast_jtag_info { ++ void __iomem *reg_base; ++ void __iomem *reg_base_scu; ++ int irq; ++ u32 flag; ++ wait_queue_head_t jtag_wq; ++ bool is_open; ++ struct device *dev; ++ struct miscdevice miscdev; ++}; ++ ++/* ++ * This structure represents a TMS cycle, as expressed in a set of bits and a ++ * count of bits (note: there are no start->end state transitions that require ++ * more than 1 byte of TMS cycles) ++ */ ++struct tms_cycle { ++ unsigned char tmsbits; ++ unsigned char count; ++}; ++ ++/* ++ * These are the string representations of the TAP states corresponding to the ++ * enums literals in JtagStateEncode ++ */ ++static const char * const c_statestr[] = {"TLR", "RTI", "SelDR", "CapDR", ++ "ShfDR", "Ex1DR", "PauDR", "Ex2DR", ++ "UpdDR", "SelIR", "CapIR", "ShfIR", ++ "Ex1IR", "PauIR", "Ex2IR", "UpdIR"}; ++ ++/* ++ * This is the complete set TMS cycles for going from any TAP state to any ++ * other TAP state, following a “shortest path” rule. ++ */ ++static const struct tms_cycle _tms_cycle_lookup[][16] = { ++/* TLR RTI SelDR CapDR SDR Ex1DR PDR Ex2DR UpdDR SelIR CapIR SIR Ex1IR PIR Ex2IR UpdIR*/ ++/* TLR */{ {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x02, 4}, {0x0a, 4}, {0x0a, 5}, {0x2a, 6}, {0x1a, 5}, {0x06, 3}, {0x06, 4}, {0x06, 5}, {0x16, 5}, {0x16, 6}, {0x56, 7}, {0x36, 6} }, ++/* RTI */{ {0x07, 3}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5} }, ++/* SelDR*/{ {0x03, 2}, {0x03, 3}, {0x00, 0}, {0x00, 1}, {0x00, 2}, {0x02, 2}, {0x02, 3}, {0x0a, 4}, {0x06, 3}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4} }, ++/* CapDR*/{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x00, 0}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} }, ++/* SDR */{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} }, ++/* Ex1DR*/{ {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x02, 3}, {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x01, 1}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6} }, ++/* PDR */{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x01, 2}, {0x05, 3}, {0x00, 0}, {0x01, 1}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} }, ++/* Ex2DR*/{ {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x00, 0}, {0x01, 1}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6} }, ++/* UpdDR*/{ {0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x00, 0}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5} }, ++/* SelIR*/{ {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x05, 4}, {0x05, 5}, {0x15, 5}, {0x15, 6}, {0x55, 7}, {0x35, 6}, {0x00, 0}, {0x00, 1}, {0x00, 2}, {0x02, 2}, {0x02, 3}, {0x0a, 4}, {0x06, 3} }, ++/* CapIR*/{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x00, 0}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2} }, ++/* SIR */{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x0f, 5}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2} }, ++/* Ex1IR*/{ {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, {0x07, 4}, {0x02, 3}, {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x01, 1} }, ++/* PIR */{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x0f, 5}, {0x01, 2}, {0x05, 3}, {0x00, 0}, {0x01, 1}, {0x03, 2} }, ++/* Ex2IR*/{ {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, {0x07, 4}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x00, 0}, {0x01, 1} }, ++/* UpdIR*/{ {0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x00, 0} }, ++}; ++ ++static const char * const regnames[] = { ++ [AST_JTAG_DATA] = "AST_JTAG_DATA", ++ [AST_JTAG_INST] = "AST_JTAG_INST", ++ [AST_JTAG_CTRL] = "AST_JTAG_CTRL", ++ [AST_JTAG_ISR] = "AST_JTAG_ISR", ++ [AST_JTAG_SW] = "AST_JTAG_SW", ++ [AST_JTAG_TCK] = "AST_JTAG_TCK", ++ [AST_JTAG_IDLE] = "AST_JTAG_IDLE", ++}; ++ ++static inline u32 ast_jtag_read(struct ast_jtag_info *ast_jtag, u32 reg) ++{ ++ u32 val = readl(ast_jtag->reg_base + reg); ++ ++ dev_dbg(ast_jtag->dev, "read:%s val = 0x%08x\n", regnames[reg], val); ++ return val; ++} ++ ++static inline void ast_jtag_write(struct ast_jtag_info *ast_jtag, u32 val, ++ u32 reg) ++{ ++ dev_dbg(ast_jtag->dev, "write:%s val = 0x%08x\n", regnames[reg], val); ++ writel(val, ast_jtag->reg_base + reg); ++} ++ ++static void ast_jtag_set_tck(struct ast_jtag_info *ast_jtag, ++ enum xfer_mode mode, uint tck) ++{ ++ u32 read_value; ++ ++ if (tck == 0) ++ tck = 1; ++ else if (tck > JTAG_TCK_DIVISOR_MASK) ++ tck = JTAG_TCK_DIVISOR_MASK; ++ read_value = ast_jtag_read(ast_jtag, AST_JTAG_TCK); ++ ast_jtag_write(ast_jtag, ++ ((read_value & ~JTAG_TCK_DIVISOR_MASK) | tck), ++ AST_JTAG_TCK); ++} ++ ++static void ast_jtag_get_tck(struct ast_jtag_info *ast_jtag, ++ enum xfer_mode mode, uint *tck) ++{ ++ *tck = FIELD_GET(JTAG_TCK_DIVISOR_MASK, ++ ast_jtag_read(ast_jtag, AST_JTAG_TCK)); ++} ++ ++/* ++ * Used only in SW mode to walk the JTAG state machine. ++ */ ++static u8 tck_cycle(struct ast_jtag_info *ast_jtag, u8 TMS, u8 TDI, ++ bool do_read) ++{ ++ u8 result = 0; ++ u32 regwriteval = JTAG_SW_MODE_EN | (TMS * JTAG_SW_MODE_TMS) ++ | (TDI * JTAG_SW_MODE_TDIO); ++ ++ /* TCK = 0 */ ++ ast_jtag_write(ast_jtag, regwriteval, AST_JTAG_SW); ++ ++ ast_jtag_read(ast_jtag, AST_JTAG_SW); ++ ++ /* TCK = 1 */ ++ ast_jtag_write(ast_jtag, JTAG_SW_MODE_TCK | regwriteval, AST_JTAG_SW); ++ ++ if (do_read) { ++ result = (ast_jtag_read(ast_jtag, AST_JTAG_SW) ++ & JTAG_SW_MODE_TDIO) ? 1 : 0; ++ } ++ return result; ++} ++ ++#define WAIT_ITERATIONS 75 ++ ++static int ast_jtag_wait_instr_pause_complete(struct ast_jtag_info *ast_jtag) ++{ ++ int res = 0; ++#ifdef USE_INTERRUPTS ++ res = wait_event_interruptible(ast_jtag->jtag_wq, ++ (ast_jtag->flag == JTAG_INST_PAUSE)); ++ ast_jtag->flag = 0; ++#else ++ u32 status = 0; ++ u32 iterations = 0; ++ ++ while ((status & JTAG_INST_PAUSE) == 0) { ++ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); ++ dev_dbg(ast_jtag->dev, "%s = 0x%08x\n", __func__, status); ++ iterations++; ++ if (iterations > WAIT_ITERATIONS) { ++ dev_err(ast_jtag->dev, ++ "ast_jtag driver timed out waiting for instruction pause complete\n"); ++ res = -EFAULT; ++ break; ++ } ++ if ((status & JTAG_DATA_COMPLETE) == 0) { ++ if (iterations % 25 == 0) ++ usleep_range(1, 5); ++ else ++ udelay(1); ++ } ++ } ++ ast_jtag_write(ast_jtag, JTAG_INST_PAUSE | (status & 0xf), ++ AST_JTAG_ISR); ++#endif ++ return res; ++} ++ ++static int ast_jtag_wait_instr_complete(struct ast_jtag_info *ast_jtag) ++{ ++ int res = 0; ++#ifdef USE_INTERRUPTS ++ res = wait_event_interruptible(ast_jtag->jtag_wq, ++ (ast_jtag->flag == JTAG_INST_COMPLETE)); ++ ast_jtag->flag = 0; ++#else ++ u32 status = 0; ++ u32 iterations = 0; ++ ++ while ((status & JTAG_INST_COMPLETE) == 0) { ++ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); ++ dev_dbg(ast_jtag->dev, "%s = 0x%08x\n", __func__, status); ++ iterations++; ++ if (iterations > WAIT_ITERATIONS) { ++ dev_err(ast_jtag->dev, ++ "ast_jtag driver timed out waiting for instruction complete\n"); ++ res = -EFAULT; ++ break; ++ } ++ if ((status & JTAG_DATA_COMPLETE) == 0) { ++ if (iterations % 25 == 0) ++ usleep_range(1, 5); ++ else ++ udelay(1); ++ } ++ } ++ ast_jtag_write(ast_jtag, JTAG_INST_COMPLETE | (status & 0xf), ++ AST_JTAG_ISR); ++#endif ++ return res; ++} ++ ++static int ast_jtag_wait_data_pause_complete(struct ast_jtag_info *ast_jtag) ++{ ++ int res = 0; ++#ifdef USE_INTERRUPTS ++ res = wait_event_interruptible(ast_jtag->jtag_wq, ++ (ast_jtag->flag == JTAG_DATA_PAUSE)); ++ ast_jtag->flag = 0; ++#else ++ u32 status = 0; ++ u32 iterations = 0; ++ ++ while ((status & JTAG_DATA_PAUSE) == 0) { ++ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); ++ dev_dbg(ast_jtag->dev, "%s = 0x%08x\n", __func__, status); ++ iterations++; ++ if (iterations > WAIT_ITERATIONS) { ++ dev_err(ast_jtag->dev, ++ "ast_jtag driver timed out waiting for data pause complete\n"); ++ res = -EFAULT; ++ break; ++ } ++ if ((status & JTAG_DATA_COMPLETE) == 0) { ++ if (iterations % 25 == 0) ++ usleep_range(1, 5); ++ else ++ udelay(1); ++ } ++ } ++ ast_jtag_write(ast_jtag, JTAG_DATA_PAUSE | (status & 0xf), ++ AST_JTAG_ISR); ++#endif ++ return res; ++} ++ ++static int ast_jtag_wait_data_complete(struct ast_jtag_info *ast_jtag) ++{ ++ int res = 0; ++#ifdef USE_INTERRUPTS ++ res = wait_event_interruptible(ast_jtag->jtag_wq, ++ (ast_jtag->flag == JTAG_DATA_COMPLETE)); ++ ast_jtag->flag = 0; ++#else ++ u32 status = 0; ++ u32 iterations = 0; ++ ++ while ((status & JTAG_DATA_COMPLETE) == 0) { ++ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); ++ dev_dbg(ast_jtag->dev, "%s = 0x%08x\n", __func__, status); ++ iterations++; ++ if (iterations > WAIT_ITERATIONS) { ++ dev_err(ast_jtag->dev, ++ "ast_jtag driver timed out waiting for data complete\n"); ++ res = -EFAULT; ++ break; ++ } ++ if ((status & JTAG_DATA_COMPLETE) == 0) { ++ if (iterations % 25 == 0) ++ usleep_range(1, 5); ++ else ++ udelay(1); ++ } ++ } ++ ast_jtag_write(ast_jtag, ++ JTAG_DATA_COMPLETE | (status & 0xf), ++ AST_JTAG_ISR); ++#endif ++ return res; ++} ++ ++static void ast_jtag_bitbang(struct ast_jtag_info *ast_jtag, ++ struct tck_bitbang *bit_bang) ++{ ++ bit_bang->tdo = tck_cycle(ast_jtag, bit_bang->tms, bit_bang->tdi, true); ++} ++ ++static void reset_tap(struct ast_jtag_info *ast_jtag, enum xfer_mode mode) ++{ ++ unsigned char i; ++ ++ if (mode == SW_MODE) { ++ for (i = 0; i < 9; i++) ++ tck_cycle(ast_jtag, 1, 0, false); ++ } else { ++ ast_jtag_write(ast_jtag, 0, AST_JTAG_SW); ++ mdelay(1); ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_FORCE_TMS, ++ AST_JTAG_CTRL); ++ mdelay(1); ++ ast_jtag_write(ast_jtag, ++ JTAG_SW_MODE_EN | JTAG_SW_MODE_TDIO, ++ AST_JTAG_SW); ++ } ++} ++ ++static int ast_jtag_set_tapstate(struct ast_jtag_info *ast_jtag, ++ enum xfer_mode mode, uint from, uint to) ++{ ++ unsigned char num_cycles; ++ unsigned char cycle; ++ unsigned char tms_bits; ++ ++ /* ++ * Ensure that the requested and current tap states are within ++ * 0 to 15. ++ */ ++ if (from >= ARRAY_SIZE(_tms_cycle_lookup[0]) || /* Column */ ++ to >= ARRAY_SIZE(_tms_cycle_lookup)) { /* row */ ++ return -1; ++ } ++ ++ dev_dbg(ast_jtag->dev, "Set TAP state: %s\n", c_statestr[to]); ++ ++ if (mode == SW_MODE) { ++ ast_jtag_write(ast_jtag, ++ JTAG_SW_MODE_EN | JTAG_SW_MODE_TDIO, ++ AST_JTAG_SW); ++ ++ if (to == jtag_tlr) { ++ reset_tap(ast_jtag, mode); ++ } else { ++ tms_bits = _tms_cycle_lookup[from][to].tmsbits; ++ num_cycles = _tms_cycle_lookup[from][to].count; ++ ++ if (num_cycles == 0) ++ return 0; ++ ++ for (cycle = 0; cycle < num_cycles; cycle++) { ++ tck_cycle(ast_jtag, (tms_bits & 1), 0, false); ++ tms_bits >>= 1; ++ } ++ } ++ } else if (to == jtag_tlr) { ++ reset_tap(ast_jtag, mode); ++ } ++ return 0; ++} ++ ++static void software_readwrite_scan(struct ast_jtag_info *ast_jtag, ++ struct scan_xfer *scan_xfer) ++{ ++ uint bit_index = 0; ++ bool is_IR = (scan_xfer->tap_state == jtag_shf_ir); ++ uint exit_tap_state = is_IR ? jtag_ex1_ir : jtag_ex1_dr; ++ unsigned char *tdi = scan_xfer->tdi; ++ unsigned char *tdo = scan_xfer->tdo; ++ ++ dev_dbg(ast_jtag->dev, "SW JTAG SHIFT %s, length = %d\n", ++ is_IR ? "IR" : "DR", scan_xfer->length); ++ ++ ast_jtag_write(ast_jtag, ++ JTAG_SW_MODE_EN | JTAG_SW_MODE_TDIO, ++ AST_JTAG_SW); ++ ++ while (bit_index < scan_xfer->length) { ++ int bit_offset = (bit_index % 8); ++ int this_input_bit = 0; ++ int tms_high_or_low; ++ int this_output_bit; ++ ++ if (bit_index / 8 < scan_xfer->tdi_bytes) { ++ /* ++ * If we are on a byte boundary, increment the byte ++ * pointers. Don't increment on 0, pointer is already ++ * on the first byte. ++ */ ++ if (bit_index % 8 == 0 && bit_index != 0) ++ tdi++; ++ this_input_bit = (*tdi >> bit_offset) & 1; ++ } ++ /* If this is the last bit, leave TMS high */ ++ tms_high_or_low = (bit_index == scan_xfer->length - 1) && ++ (scan_xfer->end_tap_state != jtag_shf_dr) && ++ (scan_xfer->end_tap_state != jtag_shf_ir); ++ this_output_bit = tck_cycle(ast_jtag, tms_high_or_low, ++ this_input_bit, !!tdo); ++ /* ++ * If it was the last bit in the scan and the end_tap_state is ++ * something other than shiftDR or shiftIR then go to Exit1. ++ * IMPORTANT Note: if the end_tap_state is ShiftIR/DR and ++ * the next call to this function is a shiftDR/IR then the ++ * driver will not change state! ++ */ ++ if (tms_high_or_low) ++ scan_xfer->tap_state = exit_tap_state; ++ if (tdo && bit_index / 8 < scan_xfer->tdo_bytes) { ++ if (bit_index % 8 == 0) { ++ if (bit_index != 0) ++ tdo++; ++ *tdo = 0; ++ } ++ *tdo |= this_output_bit << bit_offset; ++ } ++ bit_index++; ++ } ++ ast_jtag_set_tapstate(ast_jtag, scan_xfer->mode, scan_xfer->tap_state, ++ scan_xfer->end_tap_state); ++} ++ ++static int fire_ir_command(struct ast_jtag_info *ast_jtag, bool last, ++ u32 length) ++{ ++ int res; ++ ++ if (last) { ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_LAST_INST ++ | FIELD_PREP(JTAG_INST_LEN_MASK, length), ++ AST_JTAG_CTRL); ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_LAST_INST ++ | FIELD_PREP(JTAG_INST_LEN_MASK, length) ++ | JTAG_INST_EN, ++ AST_JTAG_CTRL); ++ res = ast_jtag_wait_instr_complete(ast_jtag); ++ } else { ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_IR_UPDATE ++ | FIELD_PREP(JTAG_INST_LEN_MASK, length), ++ AST_JTAG_CTRL); ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_IR_UPDATE ++ | FIELD_PREP(JTAG_INST_LEN_MASK, length) ++ | JTAG_INST_EN, ++ AST_JTAG_CTRL); ++ res = ast_jtag_wait_instr_pause_complete(ast_jtag); ++ } ++ return res; ++} ++ ++static int fire_dr_command(struct ast_jtag_info *ast_jtag, bool last, ++ u32 length) ++{ ++ int res; ++ ++ if (last) { ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_LAST_DATA ++ | FIELD_PREP(JTAG_DATA_LEN_MASK, length), ++ AST_JTAG_CTRL); ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_LAST_DATA ++ | FIELD_PREP(JTAG_DATA_LEN_MASK, length) ++ | JTAG_DATA_EN, ++ AST_JTAG_CTRL); ++ res = ast_jtag_wait_data_complete(ast_jtag); ++ } else { ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_DR_UPDATE ++ | FIELD_PREP(JTAG_DATA_LEN_MASK, length), ++ AST_JTAG_CTRL); ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_DR_UPDATE ++ | FIELD_PREP(JTAG_DATA_LEN_MASK, length) ++ | JTAG_DATA_EN, ++ AST_JTAG_CTRL); ++ res = ast_jtag_wait_data_pause_complete(ast_jtag); ++ } ++ return res; ++} ++ ++static int hardware_readwrite_scan(struct ast_jtag_info *ast_jtag, ++ struct scan_xfer *scan_xfer) ++{ ++ int res = 0; ++ u32 bits_received = 0; ++ u32 bits_to_send = 0; ++ u32 chunk_len = 0; ++ bool is_IR = (scan_xfer->tap_state == jtag_shf_ir); ++ bool is_last = false; ++ u32 length = scan_xfer->length; ++ u32 *tdi = (u32 *)scan_xfer->tdi; ++ u32 *tdo = (u32 *)scan_xfer->tdo; ++ u32 remaining_bytes; ++ int scan_end = 0; ++ u32 ast_reg = is_IR ? AST_JTAG_INST : AST_JTAG_DATA; ++ ++ dev_dbg(ast_jtag->dev, "HW JTAG SHIFT %s, length = %d\n", ++ is_IR ? "IR" : "DR", length); ++ ++ ast_jtag_write(ast_jtag, 0, AST_JTAG_SW); ++ if (scan_xfer->end_tap_state == jtag_pau_dr || ++ scan_xfer->end_tap_state == jtag_pau_ir || ++ scan_xfer->end_tap_state == jtag_shf_dr || ++ scan_xfer->end_tap_state == jtag_shf_ir) { ++ scan_end = 0; ++ } else { ++ scan_end = 1; ++ } ++ ++ while (length > 0) { ++ chunk_len = (length > 32) ? 32 : length; ++ ++ if (length <= 32 && scan_end == 1) ++ is_last = true; ++ ++ dev_dbg(ast_jtag->dev, "HW SHIFT, length=%d, scan_end=%d, chunk_len=%d, is_last=%d\n", ++ length, scan_end, chunk_len, is_last); ++ ++ remaining_bytes = (scan_xfer->length - length) / 8; ++ if (tdi && remaining_bytes < scan_xfer->tdi_bytes) { ++ bits_to_send = *tdi++; ++ ast_jtag_write(ast_jtag, bits_to_send, ast_reg); ++ } else { ++ bits_to_send = 0; ++ ast_jtag_write(ast_jtag, 0, ast_reg); ++ } ++ ++ dev_dbg(ast_jtag->dev, "HW SHIFT, len=%d chunk_len=%d is_last=%x bits_to_send=%x\n", ++ length, chunk_len, is_last, bits_to_send); ++ ++ if (is_IR) ++ res = fire_ir_command(ast_jtag, is_last, chunk_len); ++ else ++ res = fire_dr_command(ast_jtag, is_last, chunk_len); ++ if (res != 0) ++ break; ++ ++ if (tdo) { ++ bits_received = ast_jtag_read(ast_jtag, ast_reg); ++ bits_received >>= (32 - chunk_len); ++ *tdo++ = bits_received; ++ } ++ dev_dbg(ast_jtag->dev, ++ "HW SHIFT, len=%d chunk_len=%d is_last=%x bits_received=%x\n", ++ length, chunk_len, is_last, ++ bits_received); ++ length -= chunk_len; ++ } ++ return res; ++} ++ ++static int ast_jtag_readwrite_scan(struct ast_jtag_info *ast_jtag, ++ struct scan_xfer *scan_xfer) ++{ ++ int res = 0; ++ ++ if (scan_xfer->tap_state != jtag_shf_dr && ++ scan_xfer->tap_state != jtag_shf_ir) { ++ if (scan_xfer->tap_state < ARRAY_SIZE(c_statestr)) ++ dev_err(ast_jtag->dev, ++ "readwrite_scan bad current tap state = %s\n", ++ c_statestr[scan_xfer->tap_state]); ++ else ++ dev_err(ast_jtag->dev, ++ "readwrite_scan bad current tap state = %u\n", ++ scan_xfer->tap_state); ++ return -EFAULT; ++ } ++ ++ if (scan_xfer->length == 0) { ++ dev_err(ast_jtag->dev, "readwrite_scan bad length 0\n"); ++ return -EFAULT; ++ } ++ ++ if (!scan_xfer->tdi && scan_xfer->tdi_bytes != 0) { ++ dev_err(ast_jtag->dev, ++ "readwrite_scan null tdi with non-zero length %u!\n", ++ scan_xfer->tdi_bytes); ++ return -EFAULT; ++ } ++ ++ if (!scan_xfer->tdo && scan_xfer->tdo_bytes != 0) { ++ dev_err(ast_jtag->dev, ++ "readwrite_scan null tdo with non-zero length %u!\n", ++ scan_xfer->tdo_bytes); ++ return -EFAULT; ++ } ++ ++ if (!scan_xfer->tdi && !scan_xfer->tdo) { ++ dev_err(ast_jtag->dev, "readwrite_scan null tdo and tdi!\n"); ++ return -EFAULT; ++ } ++ ++ if (scan_xfer->mode == SW_MODE) ++ software_readwrite_scan(ast_jtag, scan_xfer); ++ else ++ res = hardware_readwrite_scan(ast_jtag, scan_xfer); ++ return res; ++} ++ ++#ifdef USE_INTERRUPTS ++static irqreturn_t ast_jtag_interrupt(int this_irq, void *dev_id) ++{ ++ u32 status; ++ struct ast_jtag_info *ast_jtag = dev_id; ++ ++ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); ++ ++ if (status & JTAG_INST_PAUSE) { ++ ast_jtag_write(ast_jtag, ++ JTAG_INST_PAUSE | (status & 0xf), ++ AST_JTAG_ISR); ++ ast_jtag->flag = JTAG_INST_PAUSE; ++ } ++ ++ if (status & JTAG_INST_COMPLETE) { ++ ast_jtag_write(ast_jtag, ++ JTAG_INST_COMPLETE | (status & 0xf), ++ AST_JTAG_ISR); ++ ast_jtag->flag = JTAG_INST_COMPLETE; ++ } ++ ++ if (status & JTAG_DATA_PAUSE) { ++ ast_jtag_write(ast_jtag, ++ JTAG_DATA_PAUSE | (status & 0xf), AST_JTAG_ISR); ++ ast_jtag->flag = JTAG_DATA_PAUSE; ++ } ++ ++ if (status & JTAG_DATA_COMPLETE) { ++ ast_jtag_write(ast_jtag, ++ JTAG_DATA_COMPLETE | (status & 0xf), ++ AST_JTAG_ISR); ++ ast_jtag->flag = JTAG_DATA_COMPLETE; ++ } ++ ++ if (ast_jtag->flag) { ++ wake_up_interruptible(&ast_jtag->jtag_wq); ++ return IRQ_HANDLED; ++ } else { ++ return IRQ_NONE; ++ } ++} ++#endif ++ ++static inline void ast_jtag_slave(struct ast_jtag_info *ast_jtag) ++{ ++ u32 currReg = readl((void *)(ast_jtag->reg_base_scu)); ++ ++ writel(currReg | SCU_RESET_JTAG, (void *)ast_jtag->reg_base_scu); ++} ++ ++static inline void ast_jtag_master(struct ast_jtag_info *ast_jtag) ++{ ++ u32 currReg = readl((void *)(ast_jtag->reg_base_scu)); ++ ++ writel(currReg & ~SCU_RESET_JTAG, (void *)ast_jtag->reg_base_scu); ++ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN, AST_JTAG_CTRL); ++ ast_jtag_write(ast_jtag, JTAG_SW_MODE_EN | JTAG_SW_MODE_TDIO, ++ AST_JTAG_SW); ++ ast_jtag_write(ast_jtag, JTAG_INST_PAUSE | JTAG_INST_COMPLETE | ++ JTAG_DATA_PAUSE | JTAG_DATA_COMPLETE | ++ JTAG_INST_PAUSE_EN | JTAG_INST_COMPLETE_EN | ++ JTAG_DATA_PAUSE_EN | JTAG_DATA_COMPLETE_EN, ++ AST_JTAG_ISR); /* Enable Interrupt */ ++} ++ ++static long jtag_ioctl(struct file *file, uint cmd, ulong arg) ++{ ++ int ret = 0; ++ struct ast_jtag_info *ast_jtag = file->private_data; ++ void __user *argp = (void __user *)arg; ++ struct tck_bitbang bitbang; ++ struct scan_xfer xfer; ++ struct set_tck_param set_tck_param; ++ struct get_tck_param get_tck_param; ++ struct tap_state_param tap_state_param; ++ unsigned char *kern_tdi = NULL; ++ unsigned char *kern_tdo = NULL; ++ unsigned char *user_tdi; ++ unsigned char *user_tdo; ++ ++ switch (cmd) { ++ case AST_JTAG_SET_TCK: ++ if (copy_from_user(&set_tck_param, argp, ++ sizeof(struct set_tck_param))) { ++ ret = -EFAULT; ++ } else { ++ ast_jtag_set_tck(ast_jtag, ++ set_tck_param.mode, ++ set_tck_param.tck); ++ } ++ break; ++ case AST_JTAG_GET_TCK: ++ if (copy_from_user(&get_tck_param, argp, ++ sizeof(struct get_tck_param))) ++ ret = -EFAULT; ++ else ++ ast_jtag_get_tck(ast_jtag, ++ get_tck_param.mode, ++ &get_tck_param.tck); ++ if (copy_to_user(argp, &get_tck_param, ++ sizeof(struct get_tck_param))) ++ ret = -EFAULT; ++ break; ++ case AST_JTAG_BITBANG: ++ if (copy_from_user(&bitbang, argp, ++ sizeof(struct tck_bitbang))) { ++ ret = -EFAULT; ++ } else { ++ if (bitbang.tms > 1 || bitbang.tdi > 1) ++ ret = -EFAULT; ++ else ++ ast_jtag_bitbang(ast_jtag, &bitbang); ++ } ++ if (copy_to_user(argp, &bitbang, sizeof(struct tck_bitbang))) ++ ret = -EFAULT; ++ break; ++ case AST_JTAG_SET_TAPSTATE: ++ if (copy_from_user(&tap_state_param, argp, ++ sizeof(struct tap_state_param))) ++ ret = -EFAULT; ++ else ++ ast_jtag_set_tapstate(ast_jtag, tap_state_param.mode, ++ tap_state_param.from_state, ++ tap_state_param.to_state); ++ break; ++ case AST_JTAG_READWRITESCAN: ++ if (copy_from_user(&xfer, argp, ++ sizeof(struct scan_xfer))) { ++ ret = -EFAULT; ++ } else { ++ if (xfer.tdi) { ++ user_tdi = xfer.tdi; ++ kern_tdi = memdup_user(user_tdi, ++ xfer.tdi_bytes); ++ if (IS_ERR(kern_tdi)) ++ ret = -EFAULT; ++ else ++ xfer.tdi = kern_tdi; ++ } ++ ++ if (ret == 0 && xfer.tdo) { ++ user_tdo = xfer.tdo; ++ kern_tdo = memdup_user(user_tdo, ++ xfer.tdo_bytes); ++ if (IS_ERR(kern_tdo)) ++ ret = -EFAULT; ++ else ++ xfer.tdo = kern_tdo; ++ } ++ ++ if (ret == 0) ++ ret = ast_jtag_readwrite_scan(ast_jtag, &xfer); ++ ++ kfree(kern_tdi); ++ if (kern_tdo) { ++ if (ret == 0) { ++ if (copy_to_user(user_tdo, ++ xfer.tdo, ++ xfer.tdo_bytes)) ++ ret = -EFAULT; ++ } ++ kfree(kern_tdo); ++ } ++ } ++ break; ++ default: ++ return -ENOTTY; ++ } ++ ++ return ret; ++} ++ ++static int jtag_open(struct inode *inode, struct file *file) ++{ ++ struct ast_jtag_info *ast_jtag = container_of(file->private_data, ++ struct ast_jtag_info, ++ miscdev); ++ ++ spin_lock(&jtag_state_lock); ++ if (ast_jtag->is_open) { ++ spin_unlock(&jtag_state_lock); ++ return -EBUSY; ++ } ++ ++ ast_jtag->is_open = true; ++ file->private_data = ast_jtag; ++ ast_jtag_master(ast_jtag); ++ spin_unlock(&jtag_state_lock); ++ ++ return 0; ++} ++ ++static int jtag_release(struct inode *inode, struct file *file) ++{ ++ struct ast_jtag_info *ast_jtag = file->private_data; ++ ++ spin_lock(&jtag_state_lock); ++ ast_jtag_slave(ast_jtag); ++ ast_jtag->is_open = false; ++ ++ spin_unlock(&jtag_state_lock); ++ ++ return 0; ++} ++ ++static const struct file_operations ast_jtag_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = jtag_ioctl, ++ .open = jtag_open, ++ .release = jtag_release, ++}; ++ ++static int ast_jtag_probe(struct platform_device *pdev) ++{ ++ struct resource *scu_res; ++ struct resource *jtag_res; ++ int ret = 0; ++ struct ast_jtag_info *ast_jtag; ++ ++ dev_dbg(&pdev->dev, "%s started\n", __func__); ++ ++ scu_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!scu_res) { ++ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM for SCU\n"); ++ ret = -ENOENT; ++ goto out; ++ } ++ ++ jtag_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (!jtag_res) { ++ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM for JTAG\n"); ++ ret = -ENOENT; ++ goto out; ++ } ++ ++ ast_jtag = devm_kzalloc(&pdev->dev, sizeof(*ast_jtag), GFP_KERNEL); ++ if (!ast_jtag) ++ return -ENOMEM; ++ ++ ast_jtag->reg_base_scu = devm_ioremap_resource(&pdev->dev, scu_res); ++ if (!ast_jtag->reg_base_scu) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ ast_jtag->reg_base = devm_ioremap_resource(&pdev->dev, jtag_res); ++ if (!ast_jtag->reg_base) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ ast_jtag->dev = &pdev->dev; ++ ++#ifdef USE_INTERRUPTS ++ ast_jtag->irq = platform_get_irq(pdev, 0); ++ if (ast_jtag->irq < 0) { ++ dev_err(&pdev->dev, "no irq specified.\n"); ++ ret = -ENOENT; ++ goto out; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, ast_jtag->irq, ast_jtag_interrupt, ++ IRQF_SHARED, "ast-jtag", ast_jtag); ++ if (ret) { ++ dev_err(ast_jtag->dev, "JTAG Unable to get IRQ.\n"); ++ goto out; ++ } ++#endif ++ ++ ast_jtag->flag = 0; ++ init_waitqueue_head(&ast_jtag->jtag_wq); ++ ++ ast_jtag->miscdev.minor = MISC_DYNAMIC_MINOR, ++ ast_jtag->miscdev.name = AST_JTAG_NAME, ++ ast_jtag->miscdev.fops = &ast_jtag_fops, ++ ast_jtag->miscdev.parent = &pdev->dev; ++ ret = misc_register(&ast_jtag->miscdev); ++ if (ret) { ++ dev_err(ast_jtag->dev, "Unable to register misc device.\n"); ++ goto out; ++ } ++ ++ platform_set_drvdata(pdev, ast_jtag); ++ ++ ast_jtag_slave(ast_jtag); ++ ++ dev_dbg(&pdev->dev, "%s completed\n", __func__); ++ return 0; ++ ++out: ++ dev_warn(&pdev->dev, "ast_jtag: driver init failed (ret=%d).\n", ret); ++ return ret; ++} ++ ++static int ast_jtag_remove(struct platform_device *pdev) ++{ ++ dev_dbg(&pdev->dev, "%s\n", __func__); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ dev_dbg(&pdev->dev, "JTAG driver removed successfully.\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id ast_jtag_of_match[] = { ++ { .compatible = "aspeed,ast2400-jtag", }, ++ { .compatible = "aspeed,ast2500-jtag", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ast_jtag_of_match); ++ ++static struct platform_driver ast_jtag_driver = { ++ .probe = ast_jtag_probe, ++ .remove = ast_jtag_remove, ++ .driver = { ++ .name = AST_JTAG_NAME, ++ .of_match_table = of_match_ptr(ast_jtag_of_match), ++ }, ++}; ++module_platform_driver(ast_jtag_driver); ++ ++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); ++MODULE_AUTHOR("Bryan Hunt <bryan.hunt@intel.com>"); ++MODULE_DESCRIPTION("ASPEED JTAG driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/uapi/linux/jtag_drv.h b/include/uapi/linux/jtag_drv.h +new file mode 100644 +index 000000000000..4df638f8fa43 +--- /dev/null ++++ b/include/uapi/linux/jtag_drv.h +@@ -0,0 +1,73 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2012-2017 ASPEED Technology Inc. */ ++/* Copyright (c) 2018 Intel Corporation */ ++ ++#ifndef __JTAG_DRV_H__ ++#define __JTAG_DRV_H__ ++ ++enum xfer_mode { ++ HW_MODE = 0, ++ SW_MODE ++} xfer_mode; ++ ++struct tck_bitbang { ++ __u8 tms; ++ __u8 tdi; ++ __u8 tdo; ++} __attribute__((__packed__)); ++ ++struct scan_xfer { ++ __u8 mode; ++ __u32 tap_state; ++ __u32 length; ++ __u8 *tdi; ++ __u32 tdi_bytes; ++ __u8 *tdo; ++ __u32 tdo_bytes; ++ __u32 end_tap_state; ++} __attribute__((__packed__)); ++ ++struct set_tck_param { ++ __u8 mode; ++ __u32 tck; ++} __attribute__((__packed__)); ++ ++struct get_tck_param { ++ __u8 mode; ++ __u32 tck; ++} __attribute__((__packed__)); ++ ++struct tap_state_param { ++ __u8 mode; ++ __u32 from_state; ++ __u32 to_state; ++} __attribute__((__packed__)); ++ ++enum jtag_states { ++ jtag_tlr, ++ jtag_rti, ++ jtag_sel_dr, ++ jtag_cap_dr, ++ jtag_shf_dr, ++ jtag_ex1_dr, ++ jtag_pau_dr, ++ jtag_ex2_dr, ++ jtag_upd_dr, ++ jtag_sel_ir, ++ jtag_cap_ir, ++ jtag_shf_ir, ++ jtag_ex1_ir, ++ jtag_pau_ir, ++ jtag_ex2_ir, ++ jtag_upd_ir ++} jtag_states; ++ ++#define JTAGIOC_BASE 'T' ++ ++#define AST_JTAG_SET_TCK _IOW(JTAGIOC_BASE, 3, struct set_tck_param) ++#define AST_JTAG_GET_TCK _IOR(JTAGIOC_BASE, 4, struct get_tck_param) ++#define AST_JTAG_BITBANG _IOWR(JTAGIOC_BASE, 5, struct tck_bitbang) ++#define AST_JTAG_SET_TAPSTATE _IOW(JTAGIOC_BASE, 6, struct tap_state_param) ++#define AST_JTAG_READWRITESCAN _IOWR(JTAGIOC_BASE, 7, struct scan_xfer) ++ ++#endif +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch new file mode 100644 index 000000000..e2dee0d5b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch @@ -0,0 +1,291 @@ +From a7ad8d09cdf0ec86612df0714d3e69ee92e6140b Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 20 Nov 2018 09:30:17 -0800 +Subject: [PATCH] i2c: aspeed: Improve driver to support multi-master use cases + stably + +In multi-master environment, this driver's master cannot know +exactly when peer master sends data to this driver's slave so +cases can be happened that this master tries to send data through +the master_xfer function but slave data from a peer master is still +being processed or slave xfer is started by a peer very after it +queues a master command. + +To prevent state corruption in these cases, this patch adds the +'pending' state of master and its handling code so that the pending +master xfer can be continued after slave mode session. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> +--- + drivers/i2c/busses/i2c-aspeed.c | 119 ++++++++++++++++++++++++++++++---------- + 1 file changed, 91 insertions(+), 28 deletions(-) + +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index 8dc9161ced38..d11b2ea97259 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -117,6 +117,7 @@ + + enum aspeed_i2c_master_state { + ASPEED_I2C_MASTER_INACTIVE, ++ ASPEED_I2C_MASTER_PENDING, + ASPEED_I2C_MASTER_START, + ASPEED_I2C_MASTER_TX_FIRST, + ASPEED_I2C_MASTER_TX, +@@ -126,12 +127,13 @@ enum aspeed_i2c_master_state { + }; + + enum aspeed_i2c_slave_state { +- ASPEED_I2C_SLAVE_STOP, ++ ASPEED_I2C_SLAVE_INACTIVE, + ASPEED_I2C_SLAVE_START, + ASPEED_I2C_SLAVE_READ_REQUESTED, + ASPEED_I2C_SLAVE_READ_PROCESSED, + ASPEED_I2C_SLAVE_WRITE_REQUESTED, + ASPEED_I2C_SLAVE_WRITE_RECEIVED, ++ ASPEED_I2C_SLAVE_STOP, + }; + + struct aspeed_i2c_bus { +@@ -156,6 +158,8 @@ struct aspeed_i2c_bus { + int cmd_err; + /* Protected only by i2c_lock_bus */ + int master_xfer_result; ++ /* Multi-master */ ++ bool multi_master; + #if IS_ENABLED(CONFIG_I2C_SLAVE) + struct i2c_client *slave; + enum aspeed_i2c_slave_state slave_state; +@@ -251,7 +255,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + } + + /* Slave is not currently active, irq was for someone else. */ +- if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) ++ if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) + return irq_handled; + + dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", +@@ -277,16 +281,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; + bus->slave_state = ASPEED_I2C_SLAVE_STOP; + } +- if (irq_status & ASPEED_I2CD_INTR_TX_NAK) { ++ if (irq_status & ASPEED_I2CD_INTR_TX_NAK && ++ bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { + irq_handled |= ASPEED_I2CD_INTR_TX_NAK; + bus->slave_state = ASPEED_I2C_SLAVE_STOP; + } +- if (irq_status & ASPEED_I2CD_INTR_TX_ACK) +- irq_handled |= ASPEED_I2CD_INTR_TX_ACK; + + switch (bus->slave_state) { + case ASPEED_I2C_SLAVE_READ_REQUESTED: +- if (irq_status & ASPEED_I2CD_INTR_TX_ACK) ++ if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK)) + dev_err(bus->dev, "Unexpected ACK on read request.\n"); + bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED; + i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); +@@ -294,9 +297,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); + break; + case ASPEED_I2C_SLAVE_READ_PROCESSED: +- if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK)) ++ if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { + dev_err(bus->dev, + "Expected ACK after processed read.\n"); ++ break; ++ } ++ irq_handled |= ASPEED_I2CD_INTR_TX_ACK; + i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value); + writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); + writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); +@@ -310,10 +316,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + break; + case ASPEED_I2C_SLAVE_STOP: + i2c_slave_event(slave, I2C_SLAVE_STOP, &value); ++ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; ++ break; ++ case ASPEED_I2C_SLAVE_START: ++ /* Slave was just started. Waiting for the next event. */; + break; + default: +- dev_err(bus->dev, "unhandled slave_state: %d\n", ++ dev_err(bus->dev, "unknown slave_state: %d\n", + bus->slave_state); ++ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; + break; + } + +@@ -328,7 +339,17 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) + struct i2c_msg *msg = &bus->msgs[bus->msgs_index]; + u8 slave_addr = i2c_8bit_addr_from_msg(msg); + +- bus->master_state = ASPEED_I2C_MASTER_START; ++#if IS_ENABLED(CONFIG_I2C_SLAVE) ++ /* ++ * If it's requested in the middle of a slave session, set the master ++ * state to 'pending' then H/W will continue handling this master ++ * command when the bus comes back to idle state. ++ */ ++ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) ++ bus->master_state = ASPEED_I2C_MASTER_PENDING; ++ else ++#endif /* CONFIG_I2C_SLAVE */ ++ bus->master_state = ASPEED_I2C_MASTER_START; + bus->buf_index = 0; + + if (msg->flags & I2C_M_RD) { +@@ -384,10 +405,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + bus->master_state = ASPEED_I2C_MASTER_INACTIVE; + irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE; + goto out_complete; +- } else { +- /* Master is not currently active, irq was for someone else. */ +- if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE) +- goto out_no_complete; + } + + /* +@@ -399,12 +416,33 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + if (ret) { + dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", + irq_status); +- bus->cmd_err = ret; +- bus->master_state = ASPEED_I2C_MASTER_INACTIVE; + irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); +- goto out_complete; ++ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { ++ bus->cmd_err = ret; ++ bus->master_state = ASPEED_I2C_MASTER_INACTIVE; ++ goto out_complete; ++ } + } + ++#if IS_ENABLED(CONFIG_I2C_SLAVE) ++ /* ++ * A pending master command will be started by H/W when the bus comes ++ * back to idle state after completing a slave operation so change the ++ * master state from 'pending' to 'start' at here if slave is inactive. ++ */ ++ if (bus->master_state == ASPEED_I2C_MASTER_PENDING) { ++ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) ++ goto out_no_complete; ++ ++ bus->master_state = ASPEED_I2C_MASTER_START; ++ } ++#endif /* CONFIG_I2C_SLAVE */ ++ ++ /* Master is not currently active, irq was for someone else. */ ++ if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE || ++ bus->master_state == ASPEED_I2C_MASTER_PENDING) ++ goto out_no_complete; ++ + /* We are in an invalid state; reset bus to a known state. */ + if (!bus->msgs) { + dev_err(bus->dev, "bus in unknown state. irq_status: 0x%x\n", +@@ -423,6 +461,20 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + * then update the state and handle the new state below. + */ + if (bus->master_state == ASPEED_I2C_MASTER_START) { ++#if IS_ENABLED(CONFIG_I2C_SLAVE) ++ /* ++ * If a peer master starts a xfer very after it queues a master ++ * command, change its state to 'pending' then H/W will continue ++ * the queued master xfer just after completing the slave mode ++ * session. ++ */ ++ if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) { ++ bus->master_state = ASPEED_I2C_MASTER_PENDING; ++ dev_dbg(bus->dev, ++ "master goes pending due to a slave start\n"); ++ goto out_no_complete; ++ } ++#endif /* CONFIG_I2C_SLAVE */ + if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { + if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) { + bus->cmd_err = -ENXIO; +@@ -566,7 +618,8 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) + * interrupt bits. Each case needs to be handled using corresponding + * handlers depending on the current state. + */ +- if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { ++ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE && ++ bus->master_state != ASPEED_I2C_MASTER_PENDING) { + irq_handled = aspeed_i2c_master_irq(bus, irq_remaining); + irq_remaining &= ~irq_handled; + if (irq_remaining) +@@ -601,15 +654,14 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, + { + struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); + unsigned long time_left, flags; +- int ret = 0; ++ int ret; + + spin_lock_irqsave(&bus->lock, flags); + bus->cmd_err = 0; + +- /* If bus is busy, attempt recovery. We assume a single master +- * environment. +- */ +- if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) { ++ /* If bus is busy in a single master environment, attempt recovery. */ ++ if (!bus->multi_master && ++ (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS)) { + spin_unlock_irqrestore(&bus->lock, flags); + ret = aspeed_i2c_recover_bus(bus); + if (ret) +@@ -629,10 +681,20 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, + time_left = wait_for_completion_timeout(&bus->cmd_complete, + bus->adap.timeout); + +- if (time_left == 0) ++ if (time_left == 0) { ++ /* ++ * If timed out and bus is still busy in a multi master ++ * environment, attempt recovery at here. ++ */ ++ if (bus->multi_master && ++ (readl(bus->base + ASPEED_I2C_CMD_REG) & ++ ASPEED_I2CD_BUS_BUSY_STS)) ++ ret = aspeed_i2c_recover_bus(bus); ++ + return -ETIMEDOUT; +- else +- return bus->master_xfer_result; ++ } ++ ++ return bus->master_xfer_result; + } + + static u32 aspeed_i2c_functionality(struct i2c_adapter *adap) +@@ -672,7 +734,7 @@ static int aspeed_i2c_reg_slave(struct i2c_client *client) + __aspeed_i2c_reg_slave(bus, client->addr); + + bus->slave = client; +- bus->slave_state = ASPEED_I2C_SLAVE_STOP; ++ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; + spin_unlock_irqrestore(&bus->lock, flags); + + return 0; +@@ -827,7 +889,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, + if (ret < 0) + return ret; + +- if (!of_property_read_bool(pdev->dev.of_node, "multi-master")) ++ if (of_property_read_bool(pdev->dev.of_node, "multi-master")) ++ bus->multi_master = true; ++ else + fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS; + + /* Enable Master Mode */ +@@ -930,7 +994,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) + init_completion(&bus->cmd_complete); + bus->adap.owner = THIS_MODULE; + bus->adap.retries = 0; +- bus->adap.timeout = 5 * HZ; + bus->adap.algo = &aspeed_i2c_algo; + bus->adap.dev.parent = &pdev->dev; + bus->adap.dev.of_node = pdev->dev.of_node; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch new file mode 100644 index 000000000..b735ab38b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch @@ -0,0 +1,151 @@ +From 577b65960842f4098cdfc85a311261477c051d84 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Fri, 29 Jun 2018 11:00:02 -0700 +Subject: [PATCH] Add dump debug code into I2C drivers + +This commit enables dump debug of master and slave I2C drivers. +This is only for downstream debug purpose so it shouldn't go to +the upstream. + +Usage (in case of bus 5 for an example): +echo 5 > /sys/module/i2c_aspeed/parameters/dump_debug_bus_id +echo 1 > /sys/module/i2c_aspeed/parameters/dump_debug +echo 5 > /sys/module/i2c_slave_mqueue/parameters/dump_debug_bus_id +echo 1 > /sys/module/i2c_slave_mqueue/parameters/dump_debug + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/i2c/busses/i2c-aspeed.c | 25 +++++++++++++++++++++++++ + drivers/i2c/i2c-slave-mqueue.c | 24 ++++++++++++++++++++++++ + 2 files changed, 49 insertions(+) + +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index d11b2ea97259..506d867b43d9 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -166,6 +166,19 @@ struct aspeed_i2c_bus { + #endif /* CONFIG_I2C_SLAVE */ + }; + ++static bool dump_debug __read_mostly; ++static int dump_debug_bus_id __read_mostly; ++ ++#define I2C_HEX_DUMP(bus, addr, flags, buf, len) \ ++ if (dump_debug && bus->adap.nr == dump_debug_bus_id) { \ ++ char dump_info[100] = {0,}; \ ++ snprintf(dump_info, sizeof(dump_info), \ ++ "%s (bus_id:%d, addr:0x%02x, flags:0x%02x): ", \ ++ __func__, bus->adap.nr, addr, flags); \ ++ print_hex_dump(KERN_ERR, dump_info, DUMP_PREFIX_NONE, 16, 1, \ ++ buf, len, true); \ ++ } ++ + static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus); + + static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus) +@@ -655,6 +668,7 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, + struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); + unsigned long time_left, flags; + int ret; ++ int i; + + spin_lock_irqsave(&bus->lock, flags); + bus->cmd_err = 0; +@@ -694,6 +708,11 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, + return -ETIMEDOUT; + } + ++ for (i = 0; i < num; i++) { ++ I2C_HEX_DUMP(bus, msgs[i].addr, msgs[i].flags, ++ msgs[i].buf, msgs[i].len); ++ } ++ + return bus->master_xfer_result; + } + +@@ -1061,6 +1080,12 @@ static struct platform_driver aspeed_i2c_bus_driver = { + }; + module_platform_driver(aspeed_i2c_bus_driver); + ++module_param_named(dump_debug, dump_debug, bool, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(dump_debug, "debug flag for dump printing"); ++module_param_named(dump_debug_bus_id, dump_debug_bus_id, int, ++ S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(dump_debug_bus_id, "bus id for dump debug printing"); ++ + MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>"); + MODULE_DESCRIPTION("Aspeed I2C Bus Driver"); + MODULE_LICENSE("GPL v2"); +diff --git a/drivers/i2c/i2c-slave-mqueue.c b/drivers/i2c/i2c-slave-mqueue.c +index 6014bca0ff2a..0140c0dc4c03 100644 +--- a/drivers/i2c/i2c-slave-mqueue.c ++++ b/drivers/i2c/i2c-slave-mqueue.c +@@ -21,6 +21,7 @@ struct mq_msg { + struct mq_queue { + struct bin_attribute bin; + struct kernfs_node *kn; ++ struct i2c_client *client; + + spinlock_t lock; /* spinlock for queue index handling */ + int in; +@@ -31,6 +32,19 @@ struct mq_queue { + struct mq_msg *queue; + }; + ++static bool dump_debug __read_mostly; ++static int dump_debug_bus_id __read_mostly; ++ ++#define I2C_HEX_DUMP(client, buf, len) \ ++ if (dump_debug && client->adapter->nr == dump_debug_bus_id) { \ ++ char dump_info[100] = {0,}; \ ++ snprintf(dump_info, sizeof(dump_info), \ ++ "%s (bus_id:%d, addr:0x%02x): ", \ ++ __func__, client->adapter->nr, client->addr); \ ++ print_hex_dump(KERN_ERR, dump_info, DUMP_PREFIX_NONE, 16, 1, \ ++ buf, len, true); \ ++ } ++ + static int i2c_slave_mqueue_callback(struct i2c_client *client, + enum i2c_slave_event event, u8 *val) + { +@@ -49,6 +63,7 @@ static int i2c_slave_mqueue_callback(struct i2c_client *client, + case I2C_SLAVE_WRITE_RECEIVED: + if (msg->len < MQ_MSGBUF_SIZE) { + msg->buf[msg->len++] = *val; ++ I2C_HEX_DUMP(client, val, 1); + } else { + dev_err(&client->dev, "message is truncated!\n"); + mq->truncated = 1; +@@ -101,6 +116,7 @@ static ssize_t i2c_slave_mqueue_bin_read(struct file *filp, + if (msg->len <= count) { + ret = msg->len; + memcpy(buf, msg->buf, ret); ++ I2C_HEX_DUMP(mq->client, buf, ret); + } else { + ret = -EOVERFLOW; /* Drop this HUGE one. */ + } +@@ -131,6 +147,8 @@ static int i2c_slave_mqueue_probe(struct i2c_client *client, + + BUILD_BUG_ON(!is_power_of_2(MQ_QUEUE_SIZE)); + ++ mq->client = client; ++ + buf = devm_kmalloc_array(dev, MQ_QUEUE_SIZE, MQ_MSGBUF_SIZE, + GFP_KERNEL); + if (!buf) +@@ -212,6 +230,12 @@ static struct i2c_driver i2c_slave_mqueue_driver = { + }; + module_i2c_driver(i2c_slave_mqueue_driver); + ++module_param_named(dump_debug, dump_debug, bool, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(dump_debug, "debug flag for dump printing"); ++module_param_named(dump_debug_bus_id, dump_debug_bus_id, int, ++ S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(dump_debug_bus_id, "bus id for dump debug printing"); ++ + MODULE_LICENSE("GPL v2"); + MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); + MODULE_DESCRIPTION("I2C slave mode for receiving and queuing messages"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch new file mode 100644 index 000000000..8c9d2dce0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch @@ -0,0 +1,132 @@ +From 7baa65c9bf638265874838401e27a7b6179559ff Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Wed, 2 Jan 2019 15:06:43 +0800 +Subject: [PATCH] Add high speed baud rate support for UART + +In order to support high speed baud rate(921600 bps), +the default UART clock(24MHz) needs to be switched +to 192MHz(from USB2.0 port1 PHY). + +Create a new 192M Hz clock and assign it to uart, +based on uart clock source configuration in SCU4C. + +bootloader(u-boot) will set SCU4C based on the environment configuration + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + drivers/clk/clk-aspeed.c | 41 +++++++++++++++++++++++++++----- + include/dt-bindings/clock/aspeed-clock.h | 2 ++ + 2 files changed, 37 insertions(+), 6 deletions(-) + +diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c +index 5961367..3bbb4fb 100644 +--- a/drivers/clk/clk-aspeed.c ++++ b/drivers/clk/clk-aspeed.c +@@ -14,7 +14,9 @@ + + #include <dt-bindings/clock/aspeed-clock.h> + +-#define ASPEED_NUM_CLKS 36 ++#define ASPEED_NUM_CLKS ASPEED_CLK_MAX ++#define UART_HIGH_SPEED_CLK 192000000 ++#define UART_LOW_SPEED_CLK 24000000 + + #define ASPEED_RESET2_OFFSET 32 + +@@ -28,6 +30,12 @@ + #define AST2400_HPLL_BYPASS_EN BIT(17) + #define ASPEED_MISC_CTRL 0x2c + #define UART_DIV13_EN BIT(12) ++#define ASPEED_MISC2_CTRL 0x4c ++#define UART1_HS_CLK_EN BIT(24) ++#define UART2_HS_CLK_EN BIT(25) ++#define UART3_HS_CLK_EN BIT(26) ++#define UART4_HS_CLK_EN BIT(27) ++#define UART5_HS_CLK_EN BIT(28) + #define ASPEED_STRAP 0x70 + #define CLKIN_25MHZ_EN BIT(23) + #define AST2400_CLK_SOURCE_SEL BIT(18) +@@ -425,7 +433,7 @@ static int aspeed_clk_probe(struct platform_device *pdev) + struct aspeed_reset *ar; + struct regmap *map; + struct clk_hw *hw; +- u32 val, rate; ++ u32 val, uart_clock_div; + int i, ret; + + map = syscon_node_to_regmap(dev->of_node); +@@ -460,15 +468,23 @@ static int aspeed_clk_probe(struct platform_device *pdev) + /* UART clock div13 setting */ + regmap_read(map, ASPEED_MISC_CTRL, &val); + if (val & UART_DIV13_EN) +- rate = 24000000 / 13; ++ uart_clock_div = 13; + else +- rate = 24000000; ++ uart_clock_div = 1; ++ + /* TODO: Find the parent data for the uart clock */ +- hw = clk_hw_register_fixed_rate(dev, "uart", NULL, 0, rate); ++ hw = clk_hw_register_fixed_rate(dev, "uart", NULL, 0, ++ UART_LOW_SPEED_CLK / uart_clock_div); + if (IS_ERR(hw)) + return PTR_ERR(hw); + aspeed_clk_data->hws[ASPEED_CLK_UART] = hw; + ++ hw = clk_hw_register_fixed_rate(dev, "uart-hs", "usb-port1-gate", 0, ++ UART_HIGH_SPEED_CLK / uart_clock_div); ++ if (IS_ERR(hw)) ++ return PTR_ERR(hw); ++ aspeed_clk_data->hws[ASPEED_CLK_UART_HS] = hw; ++ + /* + * Memory controller (M-PLL) PLL. This clock is configured by the + * bootloader, and is exposed to Linux as a read-only clock rate. +@@ -534,9 +550,22 @@ static int aspeed_clk_probe(struct platform_device *pdev) + * Video Engine (ECLK) mux and clock divider + */ + ++ /* Get the uart clock source configuration from SCU4C*/ ++ regmap_read(map, ASPEED_MISC2_CTRL, &val); + for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) { + const struct aspeed_gate_data *gd = &aspeed_gates[i]; + u32 gate_flags; ++ char *parent_name; ++ ++ /* For uart, needs to adjust the clock based on SCU4C value */ ++ if ((i == ASPEED_CLK_GATE_UART1CLK && (val & UART1_HS_CLK_EN)) || ++ (i == ASPEED_CLK_GATE_UART2CLK && (val & UART2_HS_CLK_EN)) || ++ (i == ASPEED_CLK_GATE_UART5CLK && (val & UART5_HS_CLK_EN)) || ++ (i == ASPEED_CLK_GATE_UART3CLK && (val & UART3_HS_CLK_EN)) || ++ (i == ASPEED_CLK_GATE_UART4CLK && (val & UART4_HS_CLK_EN))) ++ parent_name = "uart-hs"; ++ else ++ parent_name = gd->parent_name; + + /* Special case: the USB port 1 clock (bit 14) is always + * working the opposite way from the other ones. +@@ -544,7 +573,7 @@ static int aspeed_clk_probe(struct platform_device *pdev) + gate_flags = (gd->clock_idx == 14) ? 0 : CLK_GATE_SET_TO_DISABLE; + hw = aspeed_clk_hw_register_gate(dev, + gd->name, +- gd->parent_name, ++ parent_name, + gd->flags, + map, + gd->clock_idx, +diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h +index f437386..3358795 100644 +--- a/include/dt-bindings/clock/aspeed-clock.h ++++ b/include/dt-bindings/clock/aspeed-clock.h +@@ -39,6 +39,8 @@ + #define ASPEED_CLK_BCLK 33 + #define ASPEED_CLK_MPLL 34 + #define ASPEED_CLK_24M 35 ++#define ASPEED_CLK_UART_HS 36 ++#define ASPEED_CLK_MAX 37 + + #define ASPEED_RESET_XDMA 0 + #define ASPEED_RESET_MCTP 1 +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch new file mode 100644 index 000000000..539c976c7 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch @@ -0,0 +1,556 @@ +From e39e3a3e54cbe8e5a39b4148a9232f4570d009a6 Mon Sep 17 00:00:00 2001 +From: Oskar Senft <osk@google.com> +Date: Wed, 8 Aug 2018 10:15:05 -0400 +Subject: [PATCH] misc: aspeed: Add Aspeed UART routing control driver. + +This driver adds sysfs files that allow the BMC userspace to configure +how UARTs and physical serial I/O ports are routed. + +Tested: Checked correct behavior (both read & write) on TYAN S7106 +board by manually changing routing settings and confirming that bits +flow as expected. Tested for UART1 and UART3 as this board doesn't have +the other UARTs wired up in a testable way. + +Signed-off-by: Oskar Senft <osk@google.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + .../ABI/stable/sysfs-driver-aspeed-uart-routing | 14 + + Documentation/misc-devices/aspeed-uart-routing.txt | 49 +++ + arch/arm/boot/dts/aspeed-bmc-intel-purley.dts | 4 + + arch/arm/boot/dts/aspeed-g5.dtsi | 6 + + drivers/misc/Kconfig | 6 + + drivers/misc/Makefile | 1 + + drivers/misc/aspeed-uart-routing.c | 383 +++++++++++++++++++++ + 7 files changed, 463 insertions(+) + create mode 100644 Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing + create mode 100644 Documentation/misc-devices/aspeed-uart-routing.txt + create mode 100644 drivers/misc/aspeed-uart-routing.c + +diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing +new file mode 100644 +index 000000000000..5068737d9c12 +--- /dev/null ++++ b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing +@@ -0,0 +1,14 @@ ++What: /sys/bus/platform/drivers/aspeed-uart-routing/*/io* ++Date: August 2018 ++Contact: Oskar Senft <osk@google.com> ++Description: Configures the input source for the specific physical ++ serial I/O port. ++Users: OpenBMC. Proposed changes should be mailed to ++ openbmc@lists.ozlabs.org ++ ++What: /sys/bus/platform/drivers/aspeed-uart-routing/*/uart* ++Date: August 2018 ++Contact: Oskar Senft <osk@google.com> ++Description: Configures the input source for the specific UART. ++Users: OpenBMC. Proposed changes should be mailed to ++ openbmc@lists.ozlabs.org +diff --git a/Documentation/misc-devices/aspeed-uart-routing.txt b/Documentation/misc-devices/aspeed-uart-routing.txt +new file mode 100644 +index 000000000000..afaf17cb7eda +--- /dev/null ++++ b/Documentation/misc-devices/aspeed-uart-routing.txt +@@ -0,0 +1,49 @@ ++Kernel driver aspeed-uart-routing ++================================= ++ ++Supported chips: ++ASPEED AST2500 ++ ++Author: ++Google LLC ++ ++Description ++----------- ++ ++The Aspeed AST2500 allows to dynamically route the inputs for the built-in ++UARTS and physical serial I/O ports. ++ ++This allows, for example, to connect the output of UART to another UART. ++This can be used to enable host<->BMC communication via UARTs, e.g. to allow ++access to the host's serial console. ++ ++This driver is for the BMC side. The sysfs files allow the BMC userspace ++which owns the system configuration policy, to configure how UARTs and ++physical serial I/O ports are routed. ++ ++The driver provides the following files in sysfs: ++uart1 Configure the input signal to UART1. ++uart2 Configure the input signal to UART2. ++uart3 Configure the input signal to UART3. ++uart4 Configure the input signal to UART4. ++uart5 Configure the input signal to UART5. ++io1 Configure the input signal to physical serial port 1. ++io2 Configure the input signal to physical serial port 2. ++io3 Configure the input signal to physical serial port 3. ++io4 Configure the input signal to physical serial port 4. ++io5 Configure the input signal to physical serial port 5. ++ ++When read, each file shows the list of available options with the currently ++selected option marked by square brackets "[]". The list of available options ++depends on the selected file. ++ ++Example: ++$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 ++[io1] io2 io3 io4 uart2 uart3 uart4 io6 ++ ++In this case, UART1 gets its input signal from IO1 (physical serial port 1). ++ ++$ echo -n "uart3" \ ++ >/sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 ++$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 ++io1 io2 io3 io4 uart2 [uart3] uart4 io6 +diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts +index 655bb37e422f..eb05f5a2c480 100644 +--- a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts ++++ b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts +@@ -174,6 +174,10 @@ + status = "okay"; + }; + ++&uart_routing { ++ status = "okay"; ++}; ++ + &mac1 { + status = "okay"; + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 3bb31c1daf9d..92843cc1a8f4 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -482,6 +482,12 @@ + status = "disabled"; + }; + }; ++ ++ uart_routing: uart_routing@9c { ++ compatible = "aspeed,ast2500-uart-routing"; ++ reg = <0x9c 0x4>; ++ status = "disabled"; ++ }; + }; + + peci: bus@1e78b000 { +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index f2062546250c..8e2fc51dcc44 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -537,6 +537,12 @@ config MISC_RTSX + tristate + default MISC_RTSX_PCI || MISC_RTSX_USB + ++config ASPEED_UART_ROUTING ++ tristate "Aspeed ast2500 UART routing control" ++ help ++ If you want to configure UART routing on Aspeed BMC platforms, enable ++ this option. ++ + source "drivers/misc/c2port/Kconfig" + source "drivers/misc/eeprom/Kconfig" + source "drivers/misc/cb710/Kconfig" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index bb89694e6b4b..0f00eb63556c 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -56,6 +56,7 @@ obj-$(CONFIG_CXL_BASE) += cxl/ + obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o + obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o + obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o ++obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o + obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o + obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o + obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o +diff --git a/drivers/misc/aspeed-uart-routing.c b/drivers/misc/aspeed-uart-routing.c +new file mode 100644 +index 000000000000..21ef5d98c317 +--- /dev/null ++++ b/drivers/misc/aspeed-uart-routing.c +@@ -0,0 +1,383 @@ ++/* ++ * UART Routing driver for Aspeed AST2500 ++ * ++ * Copyright (c) 2018 Google LLC ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include <linux/device.h> ++#include <linux/module.h> ++#include <linux/of_address.h> ++#include <linux/of_platform.h> ++ ++/* The Aspeed AST2500 allows to dynamically route the inputs for the built-in ++ * UARTS and physical serial I/O ports. ++ * ++ * This allows, for example, to connect the output of UART to another UART. ++ * This can be used to enable host<->BMC communication via UARTs, e.g. to allow ++ * access to the host's serial console. ++ * ++ * This driver is for the BMC side. The sysfs files allow the BMC userspace ++ * which owns the system configuration policy, to configure how UARTs and ++ * physical serial I/O ports are routed. ++ */ ++ ++#define ASPEED_HICRA_IO1 "io1" ++#define ASPEED_HICRA_IO2 "io2" ++#define ASPEED_HICRA_IO3 "io3" ++#define ASPEED_HICRA_IO4 "io4" ++#define ASPEED_HICRA_IO5 "io5" ++#define ASPEED_HICRA_IO6 "io6" ++#define ASPEED_HICRA_UART1 "uart1" ++#define ASPEED_HICRA_UART2 "uart2" ++#define ASPEED_HICRA_UART3 "uart3" ++#define ASPEED_HICRA_UART4 "uart4" ++#define ASPEED_HICRA_UART5 "uart5" ++ ++struct aspeed_uart_routing { ++ struct device *dev; ++ void __iomem *regs; ++ spinlock_t lock; ++}; ++ ++struct aspeed_uart_routing_selector { ++ struct device_attribute dev_attr; ++ int shift; ++ int mask; ++ const char * const options[]; ++}; ++ ++#define to_routing_selector(_dev_attr) \ ++ container_of(_dev_attr, struct aspeed_uart_routing_selector, dev_attr) ++ ++ ++static ssize_t aspeed_uart_routing_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf); ++ ++static ssize_t aspeed_uart_routing_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count); ++ ++#define ROUTING_ATTR(_name) { \ ++ .attr = {.name = _name, \ ++ .mode = VERIFY_OCTAL_PERMISSIONS(S_IWUSR | S_IRUGO) }, \ ++ .show = aspeed_uart_routing_show, \ ++ .store = aspeed_uart_routing_store, \ ++} ++ ++static struct aspeed_uart_routing_selector uart5_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART5), ++ .shift = 28, ++ .mask = 0xf, ++ .options = { ++ ASPEED_HICRA_IO5, // 0 ++ ASPEED_HICRA_IO1, // 1 ++ ASPEED_HICRA_IO2, // 2 ++ ASPEED_HICRA_IO3, // 3 ++ ASPEED_HICRA_IO4, // 4 ++ ASPEED_HICRA_UART1, // 5 ++ ASPEED_HICRA_UART2, // 6 ++ ASPEED_HICRA_UART3, // 7 ++ ASPEED_HICRA_UART4, // 8 ++ ASPEED_HICRA_IO6, // 9 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector uart4_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART4), ++ .shift = 25, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_IO4, // 0 ++ ASPEED_HICRA_IO1, // 1 ++ ASPEED_HICRA_IO2, // 2 ++ ASPEED_HICRA_IO3, // 3 ++ ASPEED_HICRA_UART1, // 4 ++ ASPEED_HICRA_UART2, // 5 ++ ASPEED_HICRA_UART3, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector uart3_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART3), ++ .shift = 22, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_IO3, // 0 ++ ASPEED_HICRA_IO4, // 1 ++ ASPEED_HICRA_IO1, // 2 ++ ASPEED_HICRA_IO2, // 3 ++ ASPEED_HICRA_UART4, // 4 ++ ASPEED_HICRA_UART1, // 5 ++ ASPEED_HICRA_UART2, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector uart2_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART2), ++ .shift = 19, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_IO2, // 0 ++ ASPEED_HICRA_IO3, // 1 ++ ASPEED_HICRA_IO4, // 2 ++ ASPEED_HICRA_IO1, // 3 ++ ASPEED_HICRA_UART3, // 4 ++ ASPEED_HICRA_UART4, // 5 ++ ASPEED_HICRA_UART1, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector uart1_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART1), ++ .shift = 16, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_IO1, // 0 ++ ASPEED_HICRA_IO2, // 1 ++ ASPEED_HICRA_IO3, // 2 ++ ASPEED_HICRA_IO4, // 3 ++ ASPEED_HICRA_UART2, // 4 ++ ASPEED_HICRA_UART3, // 5 ++ ASPEED_HICRA_UART4, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io5_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO5), ++ .shift = 12, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART5, // 0 ++ ASPEED_HICRA_UART1, // 1 ++ ASPEED_HICRA_UART2, // 2 ++ ASPEED_HICRA_UART3, // 3 ++ ASPEED_HICRA_UART4, // 4 ++ ASPEED_HICRA_IO1, // 5 ++ ASPEED_HICRA_IO3, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io4_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO4), ++ .shift = 9, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART4, // 0 ++ ASPEED_HICRA_UART5, // 1 ++ ASPEED_HICRA_UART1, // 2 ++ ASPEED_HICRA_UART2, // 3 ++ ASPEED_HICRA_UART3, // 4 ++ ASPEED_HICRA_IO1, // 5 ++ ASPEED_HICRA_IO2, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io3_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO3), ++ .shift = 6, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART3, // 0 ++ ASPEED_HICRA_UART4, // 1 ++ ASPEED_HICRA_UART5, // 2 ++ ASPEED_HICRA_UART1, // 3 ++ ASPEED_HICRA_UART2, // 4 ++ ASPEED_HICRA_IO1, // 5 ++ ASPEED_HICRA_IO2, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io2_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO2), ++ .shift = 3, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART2, // 0 ++ ASPEED_HICRA_UART3, // 1 ++ ASPEED_HICRA_UART4, // 2 ++ ASPEED_HICRA_UART5, // 3 ++ ASPEED_HICRA_UART1, // 4 ++ ASPEED_HICRA_IO3, // 5 ++ ASPEED_HICRA_IO4, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++static struct aspeed_uart_routing_selector io1_sel = { ++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO1), ++ .shift = 0, ++ .mask = 0x7, ++ .options = { ++ ASPEED_HICRA_UART1, // 0 ++ ASPEED_HICRA_UART2, // 1 ++ ASPEED_HICRA_UART3, // 2 ++ ASPEED_HICRA_UART4, // 3 ++ ASPEED_HICRA_UART5, // 4 ++ ASPEED_HICRA_IO3, // 5 ++ ASPEED_HICRA_IO4, // 6 ++ ASPEED_HICRA_IO6, // 7 ++ NULL, // NULL termination ++ }, ++}; ++ ++ ++static struct attribute *aspeed_uart_routing_attrs[] = { ++ &uart1_sel.dev_attr.attr, ++ &uart2_sel.dev_attr.attr, ++ &uart3_sel.dev_attr.attr, ++ &uart4_sel.dev_attr.attr, ++ &uart5_sel.dev_attr.attr, ++ &io1_sel.dev_attr.attr, ++ &io2_sel.dev_attr.attr, ++ &io3_sel.dev_attr.attr, ++ &io4_sel.dev_attr.attr, ++ &io5_sel.dev_attr.attr, ++ NULL, ++}; ++ ++static const struct attribute_group aspeed_uart_routing_attr_group = { ++ .attrs = aspeed_uart_routing_attrs, ++}; ++ ++static ssize_t aspeed_uart_routing_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev); ++ struct aspeed_uart_routing_selector *sel = to_routing_selector(attr); ++ int val, pos, len; ++ ++ val = (readl(uart_routing->regs) >> sel->shift) & sel->mask; ++ ++ len = 0; ++ for (pos = 0; sel->options[pos] != NULL; ++pos) { ++ if (pos == val) { ++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, ++ "[%s] ", sel->options[pos]); ++ } else { ++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, ++ "%s ", sel->options[pos]); ++ } ++ } ++ ++ if (val >= pos) { ++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, ++ "[unknown(%d)]", val); ++ } ++ ++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, "\n"); ++ ++ return len; ++} ++ ++static ssize_t aspeed_uart_routing_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev); ++ struct aspeed_uart_routing_selector *sel = to_routing_selector(attr); ++ int val; ++ u32 reg; ++ ++ val = match_string(sel->options, -1, buf); ++ if (val < 0) { ++ dev_err(dev, "invalid value \"%s\"\n", buf); ++ return -EINVAL; ++ } ++ ++ spin_lock(&uart_routing->lock); ++ reg = readl(uart_routing->regs); ++ // Zero out existing value in specified bits. ++ reg &= ~(sel->mask << sel->shift); ++ // Set new value in specified bits. ++ reg |= (val & sel->mask) << sel->shift; ++ writel(reg, uart_routing->regs); ++ spin_unlock(&uart_routing->lock); ++ ++ return count; ++} ++ ++static int aspeed_uart_routing_probe(struct platform_device *pdev) ++{ ++ struct aspeed_uart_routing *uart_routing; ++ struct resource *res; ++ int rc; ++ ++ uart_routing = devm_kzalloc(&pdev->dev, ++ sizeof(*uart_routing), ++ GFP_KERNEL); ++ if (!uart_routing) ++ return -ENOMEM; ++ ++ spin_lock_init(&uart_routing->lock); ++ uart_routing->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ uart_routing->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(uart_routing->regs)) ++ return PTR_ERR(uart_routing->regs); ++ ++ rc = sysfs_create_group(&uart_routing->dev->kobj, ++ &aspeed_uart_routing_attr_group); ++ if (rc < 0) ++ return rc; ++ ++ platform_set_drvdata(pdev, uart_routing); ++ ++ return 0; ++} ++ ++static int aspeed_uart_routing_remove(struct platform_device *pdev) ++{ ++ struct aspeed_uart_routing *uart_routing = platform_get_drvdata(pdev); ++ ++ sysfs_remove_group(&uart_routing->dev->kobj, ++ &aspeed_uart_routing_attr_group); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_uart_routing_table[] = { ++ { .compatible = "aspeed,ast2500-uart-routing" }, ++ { }, ++}; ++ ++static struct platform_driver aspeed_uart_routing_driver = { ++ .driver = { ++ .name = "aspeed-uart-routing", ++ .of_match_table = aspeed_uart_routing_table, ++ }, ++ .probe = aspeed_uart_routing_probe, ++ .remove = aspeed_uart_routing_remove, ++}; ++ ++module_platform_driver(aspeed_uart_routing_driver); ++ ++MODULE_AUTHOR("Oskar Senft <osk@google.com>"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("Driver to configure Aspeed UART routing"); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-adpeed-Swap-the-mac-nodes-numbering.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-adpeed-Swap-the-mac-nodes-numbering.patch new file mode 100644 index 000000000..eef3bee6f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-adpeed-Swap-the-mac-nodes-numbering.patch @@ -0,0 +1,85 @@ +From 9c509b9450f641c169ee3aeb60e398c43810dcb2 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 3 Oct 2018 10:17:58 -0700 +Subject: [PATCH] arm: dts: adpeed: Swap the mac nodes numbering + +This patch swaps the numbering of mac0 and mac1 to make a dedicated +nic get assigned the first ethernet device number. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 16 ++++++++-------- + arch/arm/boot/dts/aspeed-g5.dtsi | 16 ++++++++-------- + 2 files changed, 16 insertions(+), 16 deletions(-) + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index 22eab8a952ed..004bbb08dd4a 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -101,14 +101,6 @@ + reg = <0x1e6c2000 0x80>; + }; + +- mac0: ethernet@1e660000 { +- compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; +- reg = <0x1e660000 0x180>; +- interrupts = <2>; +- clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; +- status = "disabled"; +- }; +- + mac1: ethernet@1e680000 { + compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; + reg = <0x1e680000 0x180>; +@@ -117,6 +109,14 @@ + status = "disabled"; + }; + ++ mac0: ethernet@1e660000 { ++ compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; ++ reg = <0x1e660000 0x180>; ++ interrupts = <2>; ++ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; ++ status = "disabled"; ++ }; ++ + ehci0: usb@1e6a1000 { + compatible = "aspeed,ast2400-ehci", "generic-ehci"; + reg = <0x1e6a1000 0x100>; +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 92843cc1a8f4..30a7f349feeb 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -142,14 +142,6 @@ + reg = <0x1e6c2000 0x80>; + }; + +- mac0: ethernet@1e660000 { +- compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; +- reg = <0x1e660000 0x180>; +- interrupts = <2>; +- clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; +- status = "disabled"; +- }; +- + mac1: ethernet@1e680000 { + compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; + reg = <0x1e680000 0x180>; +@@ -158,6 +150,14 @@ + status = "disabled"; + }; + ++ mac0: ethernet@1e660000 { ++ compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; ++ reg = <0x1e660000 0x180>; ++ interrupts = <2>; ++ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; ++ status = "disabled"; ++ }; ++ + ehci0: usb@1e6a1000 { + compatible = "aspeed,ast2500-ehci", "generic-ehci"; + reg = <0x1e6a1000 0x100>; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch new file mode 100644 index 000000000..51ddbb18e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch @@ -0,0 +1,249 @@ +From 1d459c15998c9a79ba7a758cef6129ed29f3b958 Mon Sep 17 00:00:00 2001 +From: cyang29 <cheng.c.yang@intel.com> +Date: Fri, 9 Nov 2018 10:24:37 +0800 +Subject: [PATCH] Implement a memory driver share memory + +Implement a memory driver for BMC to access VGA share memory. +The driver is used by MDRV2. In MDRV2 BIOS will send whole +SMBIOS table to VGA memory and BMC can get the table from VGA +memory through this driver. + +Signed-off-by: cyang29 <cheng.c.yang@intel.com> +--- + .../devicetree/bindings/misc/vga-shared-memory.txt | 20 +++ + drivers/misc/Kconfig | 10 ++ + drivers/misc/Makefile | 1 + + drivers/misc/aspeed-vga-sharedmem.c | 164 +++++++++++++++++++++ + 4 files changed, 195 insertions(+) + create mode 100644 Documentation/devicetree/bindings/misc/vga-shared-memory.txt + create mode 100644 drivers/misc/aspeed-vga-sharedmem.c + +diff --git a/Documentation/devicetree/bindings/misc/vga-shared-memory.txt b/Documentation/devicetree/bindings/misc/vga-shared-memory.txt +new file mode 100644 +index 000000000000..03f57c53e844 +--- /dev/null ++++ b/Documentation/devicetree/bindings/misc/vga-shared-memory.txt +@@ -0,0 +1,20 @@ ++* Aspeed VGA shared memory driver ++ ++Aspeed VGA shared memory driver allow user to read data from AST2500 ++VGA memory. This driver is required by ManagedDataRegionlV2 ++specification. In the spec, BIOS will transfer whole SMBIOS table to ++VGA memroy and BMC get the table from VGA memory. 0penBMC project do ++not allow to use /dev/mem for security concerns. To get the data in ++VGA shared memory in user space, implement this driver only allowed ++user to mmap limited memory area. ++ ++Required properties: ++- compatible: "aspeed,ast2500-vga-sharedmem" ++ - aspeed,ast2500-vga-sharedmem: Aspeed AST2500 family ++- reg: Should contain VGA shared memory start address and length ++ ++Example: ++vga-shared-memory { ++ compatible = "aspeed,ast2500-vga-sharedmem"; ++ reg = <0x9ff00000 0x100000>; ++}; +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 8e2fc51dcc44..1279a9674537 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -543,6 +543,16 @@ config ASPEED_UART_ROUTING + If you want to configure UART routing on Aspeed BMC platforms, enable + this option. + ++config ASPEED_VGA_SHAREDMEM ++ tristate "Aspeed VGA Shared memory" ++ depends on (ARCH_ASPEED || COMPILE_TEST) ++ help ++ To access VGA shared memory on Aspeed BMC, enable this option. ++ This driver used by ManagedDataRegionlV2 specification. In the ++ specification, BIOS will transfer whole SMBIOS table to VGA ++ memory, and BMC can get the table from VGA memory through this ++ driver. ++ + source "drivers/misc/c2port/Kconfig" + source "drivers/misc/eeprom/Kconfig" + source "drivers/misc/cb710/Kconfig" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index 0f00eb63556c..f4951a6e435b 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -62,3 +62,4 @@ obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o + obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o + obj-$(CONFIG_OCXL) += ocxl/ + obj-$(CONFIG_MISC_RTSX) += cardreader/ ++obj-$(CONFIG_ASPEED_VGA_SHAREDMEM) += aspeed-vga-sharedmem.o +diff --git a/drivers/misc/aspeed-vga-sharedmem.c b/drivers/misc/aspeed-vga-sharedmem.c +new file mode 100644 +index 000000000000..76f60cd67d3a +--- /dev/null ++++ b/drivers/misc/aspeed-vga-sharedmem.c +@@ -0,0 +1,164 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2018 Intel Corporation ++ * VGA Shared Memory driver for Aspeed AST2500 ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/miscdevice.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/of_platform.h> ++ ++#define SHAREDMEM_NAME "vgasharedmem" ++ ++struct aspeed_vga_sharedmem { ++ struct miscdevice miscdev; ++ unsigned int addr; ++ unsigned int size; ++ bool mmap_enable; ++}; ++ ++static struct aspeed_vga_sharedmem *file_sharemem(struct file *file) ++{ ++ return container_of(file->private_data, ++ struct aspeed_vga_sharedmem, miscdev); ++} ++ ++static int vga_open(struct inode *inode, struct file *file) ++{ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ struct aspeed_vga_sharedmem *vga_sharedmem = file_sharemem(file); ++ ++ if (!vga_sharedmem->mmap_enable) ++ return -EPERM; ++ ++ return 0; ++} ++ ++static int vga_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = file_sharemem(file); ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ vma->vm_flags = (vma->vm_flags & (~VM_WRITE)); ++ remap_pfn_range(vma, vma->vm_start, vga_sharedmem->addr >> PAGE_SHIFT, ++ vga_sharedmem->size, vma->vm_page_prot); ++ return 0; ++} ++ ++static ssize_t enable_mmap_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%u\n", vga_sharedmem->mmap_enable); ++} ++ ++static ssize_t enable_mmap_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = ++ dev_get_drvdata(dev); ++ bool val; ++ ++ if (kstrtobool(buf, &val)) ++ return -EINVAL; ++ ++ vga_sharedmem->mmap_enable = val; ++ ++ return count; ++} ++static DEVICE_ATTR_RW(enable_mmap); ++ ++static struct attribute *sharedmem_attrs[] = { ++ &dev_attr_enable_mmap.attr, ++ NULL ++}; ++ ++static const struct attribute_group sharedmem_attr_group = { ++ .attrs = sharedmem_attrs, ++}; ++ ++static const struct attribute_group *sharedmem_attr_groups[] = { ++ &sharedmem_attr_group, ++ NULL ++}; ++ ++static const struct file_operations vga_sharedmem_fops = { ++ .owner = THIS_MODULE, ++ .open = vga_open, ++ .mmap = vga_mmap, ++}; ++ ++static struct miscdevice vga_sharedmem_miscdev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = SHAREDMEM_NAME, ++ .fops = &vga_sharedmem_fops, ++ .groups = sharedmem_attr_groups, ++}; ++ ++static int vga_sharedmem_probe(struct platform_device *pdev) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem; ++ struct device *dev = &pdev->dev; ++ u32 reg[2]; ++ struct resource *rc; ++ ++ vga_sharedmem = devm_kzalloc(dev, sizeof(*vga_sharedmem), GFP_KERNEL); ++ if (!vga_sharedmem) ++ return -ENOMEM; ++ ++ dev_set_drvdata(&pdev->dev, vga_sharedmem); ++ ++ rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!rc) { ++ dev_err(dev, "Couldn't read size device-tree property\n"); ++ return -ENXIO; ++ } ++ ++ vga_sharedmem->addr = rc->start; ++ vga_sharedmem->size = resource_size(rc); ++ vga_sharedmem->mmap_enable = true; ++ ++ vga_sharedmem->miscdev = vga_sharedmem_miscdev; ++ ++ return misc_register(&vga_sharedmem->miscdev); ++} ++ ++static int vga_sharedmem_remove(struct platform_device *pdev) ++{ ++ struct aspeed_vga_sharedmem *vga_sharedmem = ++ dev_get_drvdata(&pdev->dev); ++ ++ misc_deregister(&vga_sharedmem->miscdev); ++ ++ return 0; ++} ++ ++static const struct of_device_id vga_sharedmem_match[] = { ++ { .compatible = "aspeed,ast2500-vga-sharedmem", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, vga_sharedmem_match); ++ ++static struct platform_driver vga_sharedmem_driver = { ++ .driver = { ++ .name = "VGA-SHAREDMEM", ++ .of_match_table = vga_sharedmem_match, ++ }, ++ .probe = vga_sharedmem_probe, ++ .remove = vga_sharedmem_remove, ++}; ++ ++module_platform_driver(vga_sharedmem_driver); ++ ++MODULE_AUTHOR("Yang Cheng <cheng.c.yang@intel.com>"); ++MODULE_DESCRIPTION("Shared VGA memory"); ++MODULE_LICENSE("GPL v2"); +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch new file mode 100644 index 000000000..83717369c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch @@ -0,0 +1,1425 @@ +From 58c3299017c5e6022fb2a2a74b662b2a4c0306f5 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 20 Nov 2018 10:14:47 -0800 +Subject: [PATCH] net/ncsi: backport ncsi patches + +net/ncsi: Allow enabling multiple packages & channels + +This series extends the NCSI driver to configure multiple packages +and/or channels simultaneously. Since the RFC series this includes a few +extra changes to fix areas in the driver that either made this harder or +were roadblocks due to deviations from the NCSI specification. + +Patches 1 & 2 fix two issues where the driver made assumptions about the +capabilities of the NCSI topology. +Patches 3 & 4 change some internal semantics slightly to make multi-mode +easier. +Patch 5 introduces a cleaner way of reconfiguring the NCSI configuration +and keeping track of channel states. +Patch 6 implements the main multi-package/multi-channel configuration, +configured via the Netlink interface. + +Readers who have an interesting NCSI setup - especially multi-package +with HWA - please test! I think I've covered all permutations but I +don't have infinite hardware to test on. + +net/ncsi: Don't enable all channels when HWA available + +NCSI hardware arbitration allows multiple packages to be enabled at once +and share the same wiring. If the NCSI driver recognises that HWA is +available it unconditionally enables all packages and channels; but that +is a configuration decision rather than something required by HWA. +Additionally the current implementation will not failover on link events +which can cause connectivity to be lost unless the interface is manually +bounced. + +Retain basic HWA support but remove the separate configuration path to +enable all channels, leaving this to be handled by a later +implementation. + +net/ncsi: Probe single packages to avoid conflict + +Currently the NCSI driver sends a select-package command to all possible +packages simultaneously to discover what packages are available. However +at this stage in the probe process the driver does not know if +hardware arbitration is available: if it isn't then this process could +cause collisions on the RMII bus when packages try to respond. + +Update the probe loop to probe each package one by one, and once +complete check if HWA is universally supported. + +net/ncsi: Don't deselect package in suspend if active + +When a package is deselected all channels of that package cease +communication. If there are other channels active on the package of the +suspended channel this will disable them as well, so only send a +deselect-package command if no other channels are active. + +net/ncsi: Don't mark configured channels inactive + +The concepts of a channel being 'active' and it having link are slightly +muddled in the NCSI driver. Tweak this slightly so that +NCSI_CHANNEL_ACTIVE represents a channel that has been configured and +enabled, and NCSI_CHANNEL_INACTIVE represents a de-configured channel. +This distinction is important because a channel can be 'active' but have +its link down; in this case the channel may still need to be configured +so that it may receive AEN link-state-change packets. + +net/ncsi: Reset channel state in ncsi_start_dev() + +When the NCSI driver is stopped with ncsi_stop_dev() the channel +monitors are stopped and the state set to "inactive". However the +channels are still configured and active from the perspective of the +network controller. We should suspend each active channel but in the +context of ncsi_stop_dev() the transmit queue has been or is about to be +stopped so we won't have time to do so. + +Instead when ncsi_start_dev() is called if the NCSI topology has already +been probed then call ncsi_reset_dev() to suspend any channels that were +previously active. This resets the network controller to a known state, +provides an up to date view of channel link state, and makes sure that +mode flags such as NCSI_MODE_TX_ENABLE are properly reset. + +In addition to ncsi_start_dev() use ncsi_reset_dev() in ncsi-netlink.c +to update the channel configuration more cleanly. + +net/ncsi: Configure multi-package, multi-channel modes with failover + +This patch extends the ncsi-netlink interface with two new commands and +three new attributes to configure multiple packages and/or channels at +once, and configure specific failover modes. + +NCSI_CMD_SET_PACKAGE mask and NCSI_CMD_SET_CHANNEL_MASK set a whitelist +of packages or channels allowed to be configured with the +NCSI_ATTR_PACKAGE_MASK and NCSI_ATTR_CHANNEL_MASK attributes +respectively. If one of these whitelists is set only packages or +channels matching the whitelist are considered for the channel queue in +ncsi_choose_active_channel(). + +These commands may also use the NCSI_ATTR_MULTI_FLAG to signal that +multiple packages or channels may be configured simultaneously. NCSI +hardware arbitration (HWA) must be available in order to enable +multi-package mode. Multi-channel mode is always available. + +If the NCSI_ATTR_CHANNEL_ID attribute is present in the +NCSI_CMD_SET_CHANNEL_MASK command the it sets the preferred channel as +with the NCSI_CMD_SET_INTERFACE command. The combination of preferred +channel and channel whitelist defines a primary channel and the allowed +failover channels. +If the NCSI_ATTR_MULTI_FLAG attribute is also present then the preferred +channel is configured for Tx/Rx and the other channels are enabled only +for Rx. + +Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + include/uapi/linux/ncsi.h | 15 ++ + net/ncsi/internal.h | 19 +- + net/ncsi/ncsi-aen.c | 75 +++++-- + net/ncsi/ncsi-manage.c | 522 ++++++++++++++++++++++++++++++++-------------- + net/ncsi/ncsi-netlink.c | 233 ++++++++++++++++++--- + net/ncsi/ncsi-rsp.c | 2 +- + 6 files changed, 660 insertions(+), 206 deletions(-) + +diff --git a/include/uapi/linux/ncsi.h b/include/uapi/linux/ncsi.h +index 0a26a5576645..a3f87c54fdb3 100644 +--- a/include/uapi/linux/ncsi.h ++++ b/include/uapi/linux/ncsi.h +@@ -26,6 +26,12 @@ + * @NCSI_CMD_SEND_CMD: send NC-SI command to network card. + * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID + * and NCSI_ATTR_CHANNEL_ID. ++ * @NCSI_CMD_SET_PACKAGE_MASK: set a whitelist of allowed packages. ++ * Requires NCSI_ATTR_IFINDEX and NCSI_ATTR_PACKAGE_MASK. ++ * @NCSI_CMD_SET_CHANNEL_MASK: set a whitelist of allowed channels. ++ * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID, and ++ * NCSI_ATTR_CHANNEL_MASK. If NCSI_ATTR_CHANNEL_ID is present it sets ++ * the primary channel. + * @NCSI_CMD_MAX: highest command number + */ + enum ncsi_nl_commands { +@@ -34,6 +40,8 @@ enum ncsi_nl_commands { + NCSI_CMD_SET_INTERFACE, + NCSI_CMD_CLEAR_INTERFACE, + NCSI_CMD_SEND_CMD, ++ NCSI_CMD_SET_PACKAGE_MASK, ++ NCSI_CMD_SET_CHANNEL_MASK, + + __NCSI_CMD_AFTER_LAST, + NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1 +@@ -48,6 +56,10 @@ enum ncsi_nl_commands { + * @NCSI_ATTR_PACKAGE_ID: package ID + * @NCSI_ATTR_CHANNEL_ID: channel ID + * @NCSI_ATTR_DATA: command payload ++ * @NCSI_ATTR_MULTI_FLAG: flag to signal that multi-mode should be enabled with ++ * NCSI_CMD_SET_PACKAGE_MASK or NCSI_CMD_SET_CHANNEL_MASK. ++ * @NCSI_ATTR_PACKAGE_MASK: 32-bit mask of allowed packages. ++ * @NCSI_ATTR_CHANNEL_MASK: 32-bit mask of allowed channels. + * @NCSI_ATTR_MAX: highest attribute number + */ + enum ncsi_nl_attrs { +@@ -57,6 +69,9 @@ enum ncsi_nl_attrs { + NCSI_ATTR_PACKAGE_ID, + NCSI_ATTR_CHANNEL_ID, + NCSI_ATTR_DATA, ++ NCSI_ATTR_MULTI_FLAG, ++ NCSI_ATTR_PACKAGE_MASK, ++ NCSI_ATTR_CHANNEL_MASK, + + __NCSI_ATTR_AFTER_LAST, + NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1 +diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h +index 1dae77c54009..9e3642b802c4 100644 +--- a/net/ncsi/internal.h ++++ b/net/ncsi/internal.h +@@ -222,6 +222,10 @@ struct ncsi_package { + unsigned int channel_num; /* Number of channels */ + struct list_head channels; /* List of chanels */ + struct list_head node; /* Form list of packages */ ++ ++ bool multi_channel; /* Enable multiple channels */ ++ u32 channel_whitelist; /* Channels to configure */ ++ struct ncsi_channel *preferred_channel; /* Primary channel */ + }; + + struct ncsi_request { +@@ -287,16 +291,16 @@ struct ncsi_dev_priv { + #define NCSI_DEV_PROBED 1 /* Finalized NCSI topology */ + #define NCSI_DEV_HWA 2 /* Enabled HW arbitration */ + #define NCSI_DEV_RESHUFFLE 4 ++#define NCSI_DEV_RESET 8 /* Reset state of NC */ + unsigned int gma_flag; /* OEM GMA flag */ + spinlock_t lock; /* Protect the NCSI device */ + #if IS_ENABLED(CONFIG_IPV6) + unsigned int inet6_addr_num; /* Number of IPv6 addresses */ + #endif ++ unsigned int package_probe_id;/* Current ID during probe */ + unsigned int package_num; /* Number of packages */ + struct list_head packages; /* List of packages */ + struct ncsi_channel *hot_channel; /* Channel was ever active */ +- struct ncsi_package *force_package; /* Force a specific package */ +- struct ncsi_channel *force_channel; /* Force a specific channel */ + struct ncsi_request requests[256]; /* Request table */ + unsigned int request_id; /* Last used request ID */ + #define NCSI_REQ_START_IDX 1 +@@ -309,6 +313,9 @@ struct ncsi_dev_priv { + struct list_head node; /* Form NCSI device list */ + #define NCSI_MAX_VLAN_VIDS 15 + struct list_head vlan_vids; /* List of active VLAN IDs */ ++ ++ bool multi_package; /* Enable multiple packages */ ++ u32 package_whitelist; /* Packages to configure */ + }; + + struct ncsi_cmd_arg { +@@ -341,6 +348,7 @@ extern spinlock_t ncsi_dev_lock; + list_for_each_entry_rcu(nc, &np->channels, node) + + /* Resources */ ++int ncsi_reset_dev(struct ncsi_dev *nd); + void ncsi_start_channel_monitor(struct ncsi_channel *nc); + void ncsi_stop_channel_monitor(struct ncsi_channel *nc); + struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np, +@@ -361,6 +369,13 @@ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, + void ncsi_free_request(struct ncsi_request *nr); + struct ncsi_dev *ncsi_find_dev(struct net_device *dev); + int ncsi_process_next_channel(struct ncsi_dev_priv *ndp); ++bool ncsi_channel_has_link(struct ncsi_channel *channel); ++bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp, ++ struct ncsi_channel *channel); ++int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp, ++ struct ncsi_package *np, ++ struct ncsi_channel *disable, ++ struct ncsi_channel *enable); + + /* Packet handlers */ + u32 ncsi_calculate_checksum(unsigned char *data, int len); +diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c +index 25e483e8278b..26d67e27551f 100644 +--- a/net/ncsi/ncsi-aen.c ++++ b/net/ncsi/ncsi-aen.c +@@ -50,13 +50,15 @@ static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h, + static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, + struct ncsi_aen_pkt_hdr *h) + { +- struct ncsi_aen_lsc_pkt *lsc; +- struct ncsi_channel *nc; ++ struct ncsi_channel *nc, *tmp; + struct ncsi_channel_mode *ncm; +- bool chained; +- int state; + unsigned long old_data, data; ++ struct ncsi_aen_lsc_pkt *lsc; ++ struct ncsi_package *np; ++ bool had_link, has_link; + unsigned long flags; ++ bool chained; ++ int state; + + /* Find the NCSI channel */ + ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc); +@@ -73,6 +75,9 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, + ncm->data[2] = data; + ncm->data[4] = ntohl(lsc->oem_status); + ++ had_link = !!(old_data & 0x1); ++ has_link = !!(data & 0x1); ++ + netdev_dbg(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n", + nc->id, data & 0x1 ? "up" : "down"); + +@@ -80,22 +85,60 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, + state = nc->state; + spin_unlock_irqrestore(&nc->lock, flags); + +- if (!((old_data ^ data) & 0x1) || chained) +- return 0; +- if (!(state == NCSI_CHANNEL_INACTIVE && (data & 0x1)) && +- !(state == NCSI_CHANNEL_ACTIVE && !(data & 0x1))) ++ if (state == NCSI_CHANNEL_INACTIVE) ++ netdev_warn(ndp->ndev.dev, ++ "NCSI: Inactive channel %u received AEN!\n", ++ nc->id); ++ ++ if ((had_link == has_link) || chained) + return 0; + +- if (!(ndp->flags & NCSI_DEV_HWA) && +- state == NCSI_CHANNEL_ACTIVE) +- ndp->flags |= NCSI_DEV_RESHUFFLE; ++ if (!ndp->multi_package && !nc->package->multi_channel) { ++ if (had_link) { ++ ndp->flags |= NCSI_DEV_RESHUFFLE; ++ ncsi_stop_channel_monitor(nc); ++ spin_lock_irqsave(&ndp->lock, flags); ++ list_add_tail_rcu(&nc->link, &ndp->channel_queue); ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ return ncsi_process_next_channel(ndp); ++ } ++ /* Configured channel came up */ ++ return 0; ++ } + +- ncsi_stop_channel_monitor(nc); +- spin_lock_irqsave(&ndp->lock, flags); +- list_add_tail_rcu(&nc->link, &ndp->channel_queue); +- spin_unlock_irqrestore(&ndp->lock, flags); ++ if (had_link) { ++ ncm = &nc->modes[NCSI_MODE_TX_ENABLE]; ++ if (ncsi_channel_is_last(ndp, nc)) { ++ /* No channels left, reconfigure */ ++ return ncsi_reset_dev(&ndp->ndev); ++ } else if (ncm->enable) { ++ /* Need to failover Tx channel */ ++ ncsi_update_tx_channel(ndp, nc->package, nc, NULL); ++ } ++ } else if (has_link && nc->package->preferred_channel == nc) { ++ /* Return Tx to preferred channel */ ++ ncsi_update_tx_channel(ndp, nc->package, NULL, nc); ++ } else if (has_link) { ++ NCSI_FOR_EACH_PACKAGE(ndp, np) { ++ NCSI_FOR_EACH_CHANNEL(np, tmp) { ++ /* Enable Tx on this channel if the current Tx ++ * channel is down. ++ */ ++ ncm = &tmp->modes[NCSI_MODE_TX_ENABLE]; ++ if (ncm->enable && ++ !ncsi_channel_has_link(tmp)) { ++ ncsi_update_tx_channel(ndp, nc->package, ++ tmp, nc); ++ break; ++ } ++ } ++ } ++ } + +- return ncsi_process_next_channel(ndp); ++ /* Leave configured channels active in a multi-channel scenario so ++ * AEN events are still received. ++ */ ++ return 0; + } + + static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp, +diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c +index bfc43b28c7a6..92e59f07f9a7 100644 +--- a/net/ncsi/ncsi-manage.c ++++ b/net/ncsi/ncsi-manage.c +@@ -28,6 +28,29 @@ + LIST_HEAD(ncsi_dev_list); + DEFINE_SPINLOCK(ncsi_dev_lock); + ++bool ncsi_channel_has_link(struct ncsi_channel *channel) ++{ ++ return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1); ++} ++ ++bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp, ++ struct ncsi_channel *channel) ++{ ++ struct ncsi_package *np; ++ struct ncsi_channel *nc; ++ ++ NCSI_FOR_EACH_PACKAGE(ndp, np) ++ NCSI_FOR_EACH_CHANNEL(np, nc) { ++ if (nc == channel) ++ continue; ++ if (nc->state == NCSI_CHANNEL_ACTIVE && ++ ncsi_channel_has_link(nc)) ++ return false; ++ } ++ ++ return true; ++} ++ + static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down) + { + struct ncsi_dev *nd = &ndp->ndev; +@@ -52,7 +75,7 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down) + continue; + } + +- if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) { ++ if (ncsi_channel_has_link(nc)) { + spin_unlock_irqrestore(&nc->lock, flags); + nd->link_up = 1; + goto report; +@@ -113,10 +136,8 @@ static void ncsi_channel_monitor(struct timer_list *t) + default: + netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n", + nc->id); +- if (!(ndp->flags & NCSI_DEV_HWA)) { +- ncsi_report_link(ndp, true); +- ndp->flags |= NCSI_DEV_RESHUFFLE; +- } ++ ncsi_report_link(ndp, true); ++ ndp->flags |= NCSI_DEV_RESHUFFLE; + + ncsi_stop_channel_monitor(nc); + +@@ -269,6 +290,7 @@ struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp, + np->ndp = ndp; + spin_lock_init(&np->lock); + INIT_LIST_HEAD(&np->channels); ++ np->channel_whitelist = UINT_MAX; + + spin_lock_irqsave(&ndp->lock, flags); + tmp = ncsi_find_package(ndp, id); +@@ -442,12 +464,14 @@ static void ncsi_request_timeout(struct timer_list *t) + static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) + { + struct ncsi_dev *nd = &ndp->ndev; +- struct ncsi_package *np = ndp->active_package; +- struct ncsi_channel *nc = ndp->active_channel; ++ struct ncsi_package *np; ++ struct ncsi_channel *nc, *tmp; + struct ncsi_cmd_arg nca; + unsigned long flags; + int ret; + ++ np = ndp->active_package; ++ nc = ndp->active_channel; + nca.ndp = ndp; + nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN; + switch (nd->state) { +@@ -523,6 +547,15 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) + if (ret) + goto error; + ++ NCSI_FOR_EACH_CHANNEL(np, tmp) { ++ /* If there is another channel active on this package ++ * do not deselect the package. ++ */ ++ if (tmp != nc && tmp->state == NCSI_CHANNEL_ACTIVE) { ++ nd->state = ncsi_dev_state_suspend_done; ++ break; ++ } ++ } + break; + case ncsi_dev_state_suspend_deselect: + ndp->pending_req_num = 1; +@@ -541,8 +574,10 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) + spin_lock_irqsave(&nc->lock, flags); + nc->state = NCSI_CHANNEL_INACTIVE; + spin_unlock_irqrestore(&nc->lock, flags); +- ncsi_process_next_channel(ndp); +- ++ if (ndp->flags & NCSI_DEV_RESET) ++ ncsi_reset_dev(nd); ++ else ++ ncsi_process_next_channel(ndp); + break; + default: + netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n", +@@ -717,13 +752,144 @@ static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id) + + #endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */ + ++/* Determine if a given channel from the channel_queue should be used for Tx */ ++static bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp, ++ struct ncsi_channel *nc) ++{ ++ struct ncsi_channel_mode *ncm; ++ struct ncsi_channel *channel; ++ struct ncsi_package *np; ++ ++ /* Check if any other channel has Tx enabled; a channel may have already ++ * been configured and removed from the channel queue. ++ */ ++ NCSI_FOR_EACH_PACKAGE(ndp, np) { ++ if (!ndp->multi_package && np != nc->package) ++ continue; ++ NCSI_FOR_EACH_CHANNEL(np, channel) { ++ ncm = &channel->modes[NCSI_MODE_TX_ENABLE]; ++ if (ncm->enable) ++ return false; ++ } ++ } ++ ++ /* This channel is the preferred channel and has link */ ++ list_for_each_entry_rcu(channel, &ndp->channel_queue, link) { ++ np = channel->package; ++ if (np->preferred_channel && ++ ncsi_channel_has_link(np->preferred_channel)) { ++ return np->preferred_channel == nc; ++ } ++ } ++ ++ /* This channel has link */ ++ if (ncsi_channel_has_link(nc)) ++ return true; ++ ++ list_for_each_entry_rcu(channel, &ndp->channel_queue, link) ++ if (ncsi_channel_has_link(channel)) ++ return false; ++ ++ /* No other channel has link; default to this one */ ++ return true; ++} ++ ++/* Change the active Tx channel in a multi-channel setup */ ++int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp, ++ struct ncsi_package *package, ++ struct ncsi_channel *disable, ++ struct ncsi_channel *enable) ++{ ++ struct ncsi_cmd_arg nca; ++ struct ncsi_channel *nc; ++ struct ncsi_package *np; ++ int ret = 0; ++ ++ if (!package->multi_channel && !ndp->multi_package) ++ netdev_warn(ndp->ndev.dev, ++ "NCSI: Trying to update Tx channel in single-channel mode\n"); ++ nca.ndp = ndp; ++ nca.req_flags = 0; ++ ++ /* Find current channel with Tx enabled */ ++ NCSI_FOR_EACH_PACKAGE(ndp, np) { ++ if (disable) ++ break; ++ if (!ndp->multi_package && np != package) ++ continue; ++ ++ NCSI_FOR_EACH_CHANNEL(np, nc) ++ if (nc->modes[NCSI_MODE_TX_ENABLE].enable) { ++ disable = nc; ++ break; ++ } ++ } ++ ++ /* Find a suitable channel for Tx */ ++ NCSI_FOR_EACH_PACKAGE(ndp, np) { ++ if (enable) ++ break; ++ if (!ndp->multi_package && np != package) ++ continue; ++ if (!(ndp->package_whitelist & (0x1 << np->id))) ++ continue; ++ ++ if (np->preferred_channel && ++ ncsi_channel_has_link(np->preferred_channel)) { ++ enable = np->preferred_channel; ++ break; ++ } ++ ++ NCSI_FOR_EACH_CHANNEL(np, nc) { ++ if (!(np->channel_whitelist & 0x1 << nc->id)) ++ continue; ++ if (nc->state != NCSI_CHANNEL_ACTIVE) ++ continue; ++ if (ncsi_channel_has_link(nc)) { ++ enable = nc; ++ break; ++ } ++ } ++ } ++ ++ if (disable == enable) ++ return -1; ++ ++ if (!enable) ++ return -1; ++ ++ if (disable) { ++ nca.channel = disable->id; ++ nca.package = disable->package->id; ++ nca.type = NCSI_PKT_CMD_DCNT; ++ ret = ncsi_xmit_cmd(&nca); ++ if (ret) ++ netdev_err(ndp->ndev.dev, ++ "Error %d sending DCNT\n", ++ ret); ++ } ++ ++ netdev_info(ndp->ndev.dev, "NCSI: channel %u enables Tx\n", enable->id); ++ ++ nca.channel = enable->id; ++ nca.package = enable->package->id; ++ nca.type = NCSI_PKT_CMD_ECNT; ++ ret = ncsi_xmit_cmd(&nca); ++ if (ret) ++ netdev_err(ndp->ndev.dev, ++ "Error %d sending ECNT\n", ++ ret); ++ ++ return ret; ++} ++ + static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) + { +- struct ncsi_dev *nd = &ndp->ndev; +- struct net_device *dev = nd->dev; + struct ncsi_package *np = ndp->active_package; + struct ncsi_channel *nc = ndp->active_channel; + struct ncsi_channel *hot_nc = NULL; ++ struct ncsi_dev *nd = &ndp->ndev; ++ struct net_device *dev = nd->dev; + struct ncsi_cmd_arg nca; + unsigned char index; + unsigned long flags; +@@ -845,20 +1011,29 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) + } else if (nd->state == ncsi_dev_state_config_ebf) { + nca.type = NCSI_PKT_CMD_EBF; + nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap; +- nd->state = ncsi_dev_state_config_ecnt; ++ if (ncsi_channel_is_tx(ndp, nc)) ++ nd->state = ncsi_dev_state_config_ecnt; ++ else ++ nd->state = ncsi_dev_state_config_ec; + #if IS_ENABLED(CONFIG_IPV6) + if (ndp->inet6_addr_num > 0 && + (nc->caps[NCSI_CAP_GENERIC].cap & + NCSI_CAP_GENERIC_MC)) + nd->state = ncsi_dev_state_config_egmf; +- else +- nd->state = ncsi_dev_state_config_ecnt; + } else if (nd->state == ncsi_dev_state_config_egmf) { + nca.type = NCSI_PKT_CMD_EGMF; + nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap; +- nd->state = ncsi_dev_state_config_ecnt; ++ if (ncsi_channel_is_tx(ndp, nc)) ++ nd->state = ncsi_dev_state_config_ecnt; ++ else ++ nd->state = ncsi_dev_state_config_ec; + #endif /* CONFIG_IPV6 */ + } else if (nd->state == ncsi_dev_state_config_ecnt) { ++ if (np->preferred_channel && ++ nc != np->preferred_channel) ++ netdev_info(ndp->ndev.dev, ++ "NCSI: Tx failed over to channel %u\n", ++ nc->id); + nca.type = NCSI_PKT_CMD_ECNT; + nd->state = ncsi_dev_state_config_ec; + } else if (nd->state == ncsi_dev_state_config_ec) { +@@ -889,6 +1064,16 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) + netdev_dbg(ndp->ndev.dev, "NCSI: channel %u config done\n", + nc->id); + spin_lock_irqsave(&nc->lock, flags); ++ nc->state = NCSI_CHANNEL_ACTIVE; ++ ++ if (ndp->flags & NCSI_DEV_RESET) { ++ /* A reset event happened during config, start it now */ ++ nc->reconfigure_needed = false; ++ spin_unlock_irqrestore(&nc->lock, flags); ++ ncsi_reset_dev(nd); ++ break; ++ } ++ + if (nc->reconfigure_needed) { + /* This channel's configuration has been updated + * part-way during the config state - start the +@@ -909,10 +1094,8 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) + + if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) { + hot_nc = nc; +- nc->state = NCSI_CHANNEL_ACTIVE; + } else { + hot_nc = NULL; +- nc->state = NCSI_CHANNEL_INACTIVE; + netdev_dbg(ndp->ndev.dev, + "NCSI: channel %u link down after config\n", + nc->id); +@@ -940,43 +1123,35 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) + + static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) + { +- struct ncsi_package *np, *force_package; +- struct ncsi_channel *nc, *found, *hot_nc, *force_channel; ++ struct ncsi_channel *nc, *found, *hot_nc; + struct ncsi_channel_mode *ncm; +- unsigned long flags; ++ unsigned long flags, cflags; ++ struct ncsi_package *np; ++ bool with_link; + + spin_lock_irqsave(&ndp->lock, flags); + hot_nc = ndp->hot_channel; +- force_channel = ndp->force_channel; +- force_package = ndp->force_package; + spin_unlock_irqrestore(&ndp->lock, flags); + +- /* Force a specific channel whether or not it has link if we have been +- * configured to do so +- */ +- if (force_package && force_channel) { +- found = force_channel; +- ncm = &found->modes[NCSI_MODE_LINK]; +- if (!(ncm->data[2] & 0x1)) +- netdev_info(ndp->ndev.dev, +- "NCSI: Channel %u forced, but it is link down\n", +- found->id); +- goto out; +- } +- +- /* The search is done once an inactive channel with up +- * link is found. ++ /* By default the search is done once an inactive channel with up ++ * link is found, unless a preferred channel is set. ++ * If multi_package or multi_channel are configured all channels in the ++ * whitelist are added to the channel queue. + */ + found = NULL; ++ with_link = false; + NCSI_FOR_EACH_PACKAGE(ndp, np) { +- if (ndp->force_package && np != ndp->force_package) ++ if (!(ndp->package_whitelist & (0x1 << np->id))) + continue; + NCSI_FOR_EACH_CHANNEL(np, nc) { +- spin_lock_irqsave(&nc->lock, flags); ++ if (!(np->channel_whitelist & (0x1 << nc->id))) ++ continue; ++ ++ spin_lock_irqsave(&nc->lock, cflags); + + if (!list_empty(&nc->link) || + nc->state != NCSI_CHANNEL_INACTIVE) { +- spin_unlock_irqrestore(&nc->lock, flags); ++ spin_unlock_irqrestore(&nc->lock, cflags); + continue; + } + +@@ -988,32 +1163,49 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) + + ncm = &nc->modes[NCSI_MODE_LINK]; + if (ncm->data[2] & 0x1) { +- spin_unlock_irqrestore(&nc->lock, flags); + found = nc; +- goto out; ++ with_link = true; + } + +- spin_unlock_irqrestore(&nc->lock, flags); ++ /* If multi_channel is enabled configure all valid ++ * channels whether or not they currently have link ++ * so they will have AENs enabled. ++ */ ++ if (with_link || np->multi_channel) { ++ spin_lock_irqsave(&ndp->lock, flags); ++ list_add_tail_rcu(&nc->link, ++ &ndp->channel_queue); ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ ++ netdev_dbg(ndp->ndev.dev, ++ "NCSI: Channel %u added to queue (link %s)\n", ++ nc->id, ++ ncm->data[2] & 0x1 ? "up" : "down"); ++ } ++ ++ spin_unlock_irqrestore(&nc->lock, cflags); ++ ++ if (with_link && !np->multi_channel) ++ break; + } ++ if (with_link && !ndp->multi_package) ++ break; + } + +- if (!found) { ++ if (list_empty(&ndp->channel_queue) && found) { ++ netdev_info(ndp->ndev.dev, ++ "NCSI: No channel with link found, configuring channel %u\n", ++ found->id); ++ spin_lock_irqsave(&ndp->lock, flags); ++ list_add_tail_rcu(&found->link, &ndp->channel_queue); ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ } else if (!found) { + netdev_warn(ndp->ndev.dev, +- "NCSI: No channel found with link\n"); ++ "NCSI: No channel found to configure!\n"); + ncsi_report_link(ndp, true); + return -ENODEV; + } + +- ncm = &found->modes[NCSI_MODE_LINK]; +- netdev_dbg(ndp->ndev.dev, +- "NCSI: Channel %u added to queue (link %s)\n", +- found->id, ncm->data[2] & 0x1 ? "up" : "down"); +- +-out: +- spin_lock_irqsave(&ndp->lock, flags); +- list_add_tail_rcu(&found->link, &ndp->channel_queue); +- spin_unlock_irqrestore(&ndp->lock, flags); +- + return ncsi_process_next_channel(ndp); + } + +@@ -1050,35 +1242,6 @@ static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp) + return false; + } + +-static int ncsi_enable_hwa(struct ncsi_dev_priv *ndp) +-{ +- struct ncsi_package *np; +- struct ncsi_channel *nc; +- unsigned long flags; +- +- /* Move all available channels to processing queue */ +- spin_lock_irqsave(&ndp->lock, flags); +- NCSI_FOR_EACH_PACKAGE(ndp, np) { +- NCSI_FOR_EACH_CHANNEL(np, nc) { +- WARN_ON_ONCE(nc->state != NCSI_CHANNEL_INACTIVE || +- !list_empty(&nc->link)); +- ncsi_stop_channel_monitor(nc); +- list_add_tail_rcu(&nc->link, &ndp->channel_queue); +- } +- } +- spin_unlock_irqrestore(&ndp->lock, flags); +- +- /* We can have no channels in extremely case */ +- if (list_empty(&ndp->channel_queue)) { +- netdev_err(ndp->ndev.dev, +- "NCSI: No available channels for HWA\n"); +- ncsi_report_link(ndp, false); +- return -ENOENT; +- } +- +- return ncsi_process_next_channel(ndp); +-} +- + static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) + { + struct ncsi_dev *nd = &ndp->ndev; +@@ -1110,70 +1273,28 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) + nd->state = ncsi_dev_state_probe_package; + break; + case ncsi_dev_state_probe_package: +- ndp->pending_req_num = 16; ++ ndp->pending_req_num = 1; + +- /* Select all possible packages */ + nca.type = NCSI_PKT_CMD_SP; + nca.bytes[0] = 1; ++ nca.package = ndp->package_probe_id; + nca.channel = NCSI_RESERVED_CHANNEL; +- for (index = 0; index < 8; index++) { +- nca.package = index; +- ret = ncsi_xmit_cmd(&nca); +- if (ret) +- goto error; +- } +- +- /* Disable all possible packages */ +- nca.type = NCSI_PKT_CMD_DP; +- for (index = 0; index < 8; index++) { +- nca.package = index; +- ret = ncsi_xmit_cmd(&nca); +- if (ret) +- goto error; +- } +- ++ ret = ncsi_xmit_cmd(&nca); ++ if (ret) ++ goto error; + nd->state = ncsi_dev_state_probe_channel; + break; + case ncsi_dev_state_probe_channel: +- if (!ndp->active_package) +- ndp->active_package = list_first_or_null_rcu( +- &ndp->packages, struct ncsi_package, node); +- else if (list_is_last(&ndp->active_package->node, +- &ndp->packages)) +- ndp->active_package = NULL; +- else +- ndp->active_package = list_next_entry( +- ndp->active_package, node); +- +- /* All available packages and channels are enumerated. The +- * enumeration happens for once when the NCSI interface is +- * started. So we need continue to start the interface after +- * the enumeration. +- * +- * We have to choose an active channel before configuring it. +- * Note that we possibly don't have active channel in extreme +- * situation. +- */ ++ ndp->active_package = ncsi_find_package(ndp, ++ ndp->package_probe_id); + if (!ndp->active_package) { +- ndp->flags |= NCSI_DEV_PROBED; +- if (ncsi_check_hwa(ndp)) +- ncsi_enable_hwa(ndp); +- else +- ncsi_choose_active_channel(ndp); +- return; ++ /* No response */ ++ nd->state = ncsi_dev_state_probe_dp; ++ schedule_work(&ndp->work); ++ break; + } +- +- /* Select the active package */ +- ndp->pending_req_num = 1; +- nca.type = NCSI_PKT_CMD_SP; +- nca.bytes[0] = 1; +- nca.package = ndp->active_package->id; +- nca.channel = NCSI_RESERVED_CHANNEL; +- ret = ncsi_xmit_cmd(&nca); +- if (ret) +- goto error; +- + nd->state = ncsi_dev_state_probe_cis; ++ schedule_work(&ndp->work); + break; + case ncsi_dev_state_probe_cis: + ndp->pending_req_num = NCSI_RESERVED_CHANNEL; +@@ -1222,22 +1343,35 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) + case ncsi_dev_state_probe_dp: + ndp->pending_req_num = 1; + +- /* Deselect the active package */ ++ /* Deselect the current package */ + nca.type = NCSI_PKT_CMD_DP; +- nca.package = ndp->active_package->id; ++ nca.package = ndp->package_probe_id; + nca.channel = NCSI_RESERVED_CHANNEL; + ret = ncsi_xmit_cmd(&nca); + if (ret) + goto error; + +- /* Scan channels in next package */ +- nd->state = ncsi_dev_state_probe_channel; ++ /* Probe next package */ ++ ndp->package_probe_id++; ++ if (ndp->package_probe_id >= 8) { ++ /* Probe finished */ ++ ndp->flags |= NCSI_DEV_PROBED; ++ break; ++ } ++ nd->state = ncsi_dev_state_probe_package; ++ ndp->active_package = NULL; + break; + default: + netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n", + nd->state); + } + ++ if (ndp->flags & NCSI_DEV_PROBED) { ++ /* Check if all packages have HWA support */ ++ ncsi_check_hwa(ndp); ++ ncsi_choose_active_channel(ndp); ++ } ++ + return; + error: + netdev_err(ndp->ndev.dev, +@@ -1556,6 +1690,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev, + INIT_LIST_HEAD(&ndp->channel_queue); + INIT_LIST_HEAD(&ndp->vlan_vids); + INIT_WORK(&ndp->work, ncsi_dev_work); ++ ndp->package_whitelist = UINT_MAX; + + /* Initialize private NCSI device */ + spin_lock_init(&ndp->lock); +@@ -1592,26 +1727,19 @@ EXPORT_SYMBOL_GPL(ncsi_register_dev); + int ncsi_start_dev(struct ncsi_dev *nd) + { + struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd); +- int ret; + + if (nd->state != ncsi_dev_state_registered && + nd->state != ncsi_dev_state_functional) + return -ENOTTY; + + if (!(ndp->flags & NCSI_DEV_PROBED)) { ++ ndp->package_probe_id = 0; + nd->state = ncsi_dev_state_probe; + schedule_work(&ndp->work); + return 0; + } + +- if (ndp->flags & NCSI_DEV_HWA) { +- netdev_info(ndp->ndev.dev, "NCSI: Enabling HWA mode\n"); +- ret = ncsi_enable_hwa(ndp); +- } else { +- ret = ncsi_choose_active_channel(ndp); +- } +- +- return ret; ++ return ncsi_reset_dev(nd); + } + EXPORT_SYMBOL_GPL(ncsi_start_dev); + +@@ -1624,7 +1752,10 @@ void ncsi_stop_dev(struct ncsi_dev *nd) + int old_state; + unsigned long flags; + +- /* Stop the channel monitor and reset channel's state */ ++ /* Stop the channel monitor on any active channels. Don't reset the ++ * channel state so we know which were active when ncsi_start_dev() ++ * is next called. ++ */ + NCSI_FOR_EACH_PACKAGE(ndp, np) { + NCSI_FOR_EACH_CHANNEL(np, nc) { + ncsi_stop_channel_monitor(nc); +@@ -1632,7 +1763,6 @@ void ncsi_stop_dev(struct ncsi_dev *nd) + spin_lock_irqsave(&nc->lock, flags); + chained = !list_empty(&nc->link); + old_state = nc->state; +- nc->state = NCSI_CHANNEL_INACTIVE; + spin_unlock_irqrestore(&nc->lock, flags); + + WARN_ON_ONCE(chained || +@@ -1645,6 +1775,92 @@ void ncsi_stop_dev(struct ncsi_dev *nd) + } + EXPORT_SYMBOL_GPL(ncsi_stop_dev); + ++int ncsi_reset_dev(struct ncsi_dev *nd) ++{ ++ struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd); ++ struct ncsi_channel *nc, *active, *tmp; ++ struct ncsi_package *np; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ndp->lock, flags); ++ ++ if (!(ndp->flags & NCSI_DEV_RESET)) { ++ /* Haven't been called yet, check states */ ++ switch (nd->state & ncsi_dev_state_major) { ++ case ncsi_dev_state_registered: ++ case ncsi_dev_state_probe: ++ /* Not even probed yet - do nothing */ ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ return 0; ++ case ncsi_dev_state_suspend: ++ case ncsi_dev_state_config: ++ /* Wait for the channel to finish its suspend/config ++ * operation; once it finishes it will check for ++ * NCSI_DEV_RESET and reset the state. ++ */ ++ ndp->flags |= NCSI_DEV_RESET; ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ return 0; ++ } ++ } else { ++ switch (nd->state) { ++ case ncsi_dev_state_suspend_done: ++ case ncsi_dev_state_config_done: ++ case ncsi_dev_state_functional: ++ /* Ok */ ++ break; ++ default: ++ /* Current reset operation happening */ ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ return 0; ++ } ++ } ++ ++ if (!list_empty(&ndp->channel_queue)) { ++ /* Clear any channel queue we may have interrupted */ ++ list_for_each_entry_safe(nc, tmp, &ndp->channel_queue, link) ++ list_del_init(&nc->link); ++ } ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ ++ active = NULL; ++ NCSI_FOR_EACH_PACKAGE(ndp, np) { ++ NCSI_FOR_EACH_CHANNEL(np, nc) { ++ spin_lock_irqsave(&nc->lock, flags); ++ ++ if (nc->state == NCSI_CHANNEL_ACTIVE) { ++ active = nc; ++ nc->state = NCSI_CHANNEL_INVISIBLE; ++ spin_unlock_irqrestore(&nc->lock, flags); ++ ncsi_stop_channel_monitor(nc); ++ break; ++ } ++ ++ spin_unlock_irqrestore(&nc->lock, flags); ++ } ++ if (active) ++ break; ++ } ++ ++ if (!active) { ++ /* Done */ ++ spin_lock_irqsave(&ndp->lock, flags); ++ ndp->flags &= ~NCSI_DEV_RESET; ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ return ncsi_choose_active_channel(ndp); ++ } ++ ++ spin_lock_irqsave(&ndp->lock, flags); ++ ndp->flags |= NCSI_DEV_RESET; ++ ndp->active_channel = active; ++ ndp->active_package = active->package; ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ ++ nd->state = ncsi_dev_state_suspend; ++ schedule_work(&ndp->work); ++ return 0; ++} ++ + void ncsi_unregister_dev(struct ncsi_dev *nd) + { + struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd); +diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c +index 33314381b4f5..5d782445d2fc 100644 +--- a/net/ncsi/ncsi-netlink.c ++++ b/net/ncsi/ncsi-netlink.c +@@ -30,6 +30,9 @@ static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = { + [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, + [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, + [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 }, ++ [NCSI_ATTR_MULTI_FLAG] = { .type = NLA_FLAG }, ++ [NCSI_ATTR_PACKAGE_MASK] = { .type = NLA_U32 }, ++ [NCSI_ATTR_CHANNEL_MASK] = { .type = NLA_U32 }, + }; + + static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) +@@ -69,7 +72,7 @@ static int ncsi_write_channel_info(struct sk_buff *skb, + nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); + if (nc->state == NCSI_CHANNEL_ACTIVE) + nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); +- if (ndp->force_channel == nc) ++ if (nc == nc->package->preferred_channel) + nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); + + nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); +@@ -114,7 +117,7 @@ static int ncsi_write_package_info(struct sk_buff *skb, + if (!pnest) + return -ENOMEM; + nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); +- if (ndp->force_package == np) ++ if ((0x1 << np->id) == ndp->package_whitelist) + nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); + cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST); + if (!cnest) { +@@ -290,49 +293,58 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) + package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); + package = NULL; + +- spin_lock_irqsave(&ndp->lock, flags); +- + NCSI_FOR_EACH_PACKAGE(ndp, np) + if (np->id == package_id) + package = np; + if (!package) { + /* The user has set a package that does not exist */ +- spin_unlock_irqrestore(&ndp->lock, flags); + return -ERANGE; + } + + channel = NULL; +- if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { +- /* Allow any channel */ +- channel_id = NCSI_RESERVED_CHANNEL; +- } else { ++ if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { + channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); + NCSI_FOR_EACH_CHANNEL(package, nc) +- if (nc->id == channel_id) ++ if (nc->id == channel_id) { + channel = nc; ++ break; ++ } ++ if (!channel) { ++ netdev_info(ndp->ndev.dev, ++ "NCSI: Channel %u does not exist!\n", ++ channel_id); ++ return -ERANGE; ++ } + } + +- if (channel_id != NCSI_RESERVED_CHANNEL && !channel) { +- /* The user has set a channel that does not exist on this +- * package +- */ +- spin_unlock_irqrestore(&ndp->lock, flags); +- netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n", +- channel_id); +- return -ERANGE; +- } +- +- ndp->force_package = package; +- ndp->force_channel = channel; ++ spin_lock_irqsave(&ndp->lock, flags); ++ ndp->package_whitelist = 0x1 << package->id; ++ ndp->multi_package = false; + spin_unlock_irqrestore(&ndp->lock, flags); + +- netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n", +- package_id, channel_id, +- channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : ""); ++ spin_lock_irqsave(&package->lock, flags); ++ package->multi_channel = false; ++ if (channel) { ++ package->channel_whitelist = 0x1 << channel->id; ++ package->preferred_channel = channel; ++ } else { ++ /* Allow any channel */ ++ package->channel_whitelist = UINT_MAX; ++ package->preferred_channel = NULL; ++ } ++ spin_unlock_irqrestore(&package->lock, flags); ++ ++ if (channel) ++ netdev_info(ndp->ndev.dev, ++ "Set package 0x%x, channel 0x%x as preferred\n", ++ package_id, channel_id); ++ else ++ netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n", ++ package_id); + +- /* Bounce the NCSI channel to set changes */ +- ncsi_stop_dev(&ndp->ndev); +- ncsi_start_dev(&ndp->ndev); ++ /* Update channel configuration */ ++ if (!(ndp->flags & NCSI_DEV_RESET)) ++ ncsi_reset_dev(&ndp->ndev); + + return 0; + } +@@ -340,6 +352,7 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) + static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) + { + struct ncsi_dev_priv *ndp; ++ struct ncsi_package *np; + unsigned long flags; + + if (!info || !info->attrs) +@@ -353,16 +366,24 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) + if (!ndp) + return -ENODEV; + +- /* Clear any override */ ++ /* Reset any whitelists and disable multi mode */ + spin_lock_irqsave(&ndp->lock, flags); +- ndp->force_package = NULL; +- ndp->force_channel = NULL; ++ ndp->package_whitelist = UINT_MAX; ++ ndp->multi_package = false; + spin_unlock_irqrestore(&ndp->lock, flags); ++ ++ NCSI_FOR_EACH_PACKAGE(ndp, np) { ++ spin_lock_irqsave(&np->lock, flags); ++ np->multi_channel = false; ++ np->channel_whitelist = UINT_MAX; ++ np->preferred_channel = NULL; ++ spin_unlock_irqrestore(&np->lock, flags); ++ } + netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); + +- /* Bounce the NCSI channel to set changes */ +- ncsi_stop_dev(&ndp->ndev); +- ncsi_start_dev(&ndp->ndev); ++ /* Update channel configuration */ ++ if (!(ndp->flags & NCSI_DEV_RESET)) ++ ncsi_reset_dev(&ndp->ndev); + + return 0; + } +@@ -563,6 +584,138 @@ int ncsi_send_netlink_err(struct net_device *dev, + return nlmsg_unicast(net->genl_sock, skb, snd_portid); + } + ++static int ncsi_set_package_mask_nl(struct sk_buff *msg, ++ struct genl_info *info) ++{ ++ struct ncsi_dev_priv *ndp; ++ unsigned long flags; ++ int rc; ++ ++ if (!info || !info->attrs) ++ return -EINVAL; ++ ++ if (!info->attrs[NCSI_ATTR_IFINDEX]) ++ return -EINVAL; ++ ++ if (!info->attrs[NCSI_ATTR_PACKAGE_MASK]) ++ return -EINVAL; ++ ++ ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), ++ nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); ++ if (!ndp) ++ return -ENODEV; ++ ++ spin_lock_irqsave(&ndp->lock, flags); ++ if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { ++ if (ndp->flags & NCSI_DEV_HWA) { ++ ndp->multi_package = true; ++ rc = 0; ++ } else { ++ netdev_err(ndp->ndev.dev, ++ "NCSI: Can't use multiple packages without HWA\n"); ++ rc = -EPERM; ++ } ++ } else { ++ ndp->multi_package = false; ++ rc = 0; ++ } ++ ++ if (!rc) ++ ndp->package_whitelist = ++ nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]); ++ spin_unlock_irqrestore(&ndp->lock, flags); ++ ++ if (!rc) { ++ /* Update channel configuration */ ++ if (!(ndp->flags & NCSI_DEV_RESET)) ++ ncsi_reset_dev(&ndp->ndev); ++ } ++ ++ return rc; ++} ++ ++static int ncsi_set_channel_mask_nl(struct sk_buff *msg, ++ struct genl_info *info) ++{ ++ struct ncsi_package *np, *package; ++ struct ncsi_channel *nc, *channel; ++ u32 package_id, channel_id; ++ struct ncsi_dev_priv *ndp; ++ unsigned long flags; ++ ++ if (!info || !info->attrs) ++ return -EINVAL; ++ ++ if (!info->attrs[NCSI_ATTR_IFINDEX]) ++ return -EINVAL; ++ ++ if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) ++ return -EINVAL; ++ ++ if (!info->attrs[NCSI_ATTR_CHANNEL_MASK]) ++ return -EINVAL; ++ ++ ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), ++ nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); ++ if (!ndp) ++ return -ENODEV; ++ ++ package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); ++ package = NULL; ++ NCSI_FOR_EACH_PACKAGE(ndp, np) ++ if (np->id == package_id) { ++ package = np; ++ break; ++ } ++ if (!package) ++ return -ERANGE; ++ ++ spin_lock_irqsave(&package->lock, flags); ++ ++ channel = NULL; ++ if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { ++ channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); ++ NCSI_FOR_EACH_CHANNEL(np, nc) ++ if (nc->id == channel_id) { ++ channel = nc; ++ break; ++ } ++ if (!channel) { ++ spin_unlock_irqrestore(&package->lock, flags); ++ return -ERANGE; ++ } ++ netdev_dbg(ndp->ndev.dev, ++ "NCSI: Channel %u set as preferred channel\n", ++ channel->id); ++ } ++ ++ package->channel_whitelist = ++ nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]); ++ if (package->channel_whitelist == 0) ++ netdev_dbg(ndp->ndev.dev, ++ "NCSI: Package %u set to all channels disabled\n", ++ package->id); ++ ++ package->preferred_channel = channel; ++ ++ if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { ++ package->multi_channel = true; ++ netdev_info(ndp->ndev.dev, ++ "NCSI: Multi-channel enabled on package %u\n", ++ package_id); ++ } else { ++ package->multi_channel = false; ++ } ++ ++ spin_unlock_irqrestore(&package->lock, flags); ++ ++ /* Update channel configuration */ ++ if (!(ndp->flags & NCSI_DEV_RESET)) ++ ncsi_reset_dev(&ndp->ndev); ++ ++ return 0; ++} ++ + static const struct genl_ops ncsi_ops[] = { + { + .cmd = NCSI_CMD_PKG_INFO, +@@ -589,6 +742,18 @@ static const struct genl_ops ncsi_ops[] = { + .doit = ncsi_send_cmd_nl, + .flags = GENL_ADMIN_PERM, + }, ++ { ++ .cmd = NCSI_CMD_SET_PACKAGE_MASK, ++ .policy = ncsi_genl_policy, ++ .doit = ncsi_set_package_mask_nl, ++ .flags = GENL_ADMIN_PERM, ++ }, ++ { ++ .cmd = NCSI_CMD_SET_CHANNEL_MASK, ++ .policy = ncsi_genl_policy, ++ .doit = ncsi_set_channel_mask_nl, ++ .flags = GENL_ADMIN_PERM, ++ }, + }; + + static struct genl_family ncsi_genl_family __ro_after_init = { +diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c +index 77e07ba3f493..de7737a27889 100644 +--- a/net/ncsi/ncsi-rsp.c ++++ b/net/ncsi/ncsi-rsp.c +@@ -256,7 +256,7 @@ static int ncsi_rsp_handler_dcnt(struct ncsi_request *nr) + if (!ncm->enable) + return 0; + +- ncm->enable = 1; ++ ncm->enable = 0; + return 0; + } + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch new file mode 100644 index 000000000..b618c49da --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch @@ -0,0 +1,1982 @@ +From ba52b9e7f76879f888afce1f8e7e5ff180b7849b Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Fri, 9 Nov 2018 11:32:27 -0800 +Subject: [PATCH] Add Aspeed Video Engine Driver + +The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs +can capture and compress video data from digital or analog sources. With +the Aspeed chip acting a service processor, the Video Engine can capture +the host processor graphics output. + +Add a V4L2 driver to capture video data and compress it to JPEG images. +Make the video frames available through the V4L2 streaming interface. + +Signed-off-by: Eddie James <eajames@linux.ibm.com> +Reviewed-by: Rob Herring <robh@kernel.org> +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> +--- + .../devicetree/bindings/media/aspeed-video.txt | 26 + + MAINTAINERS | 8 + + arch/arm/boot/dts/aspeed-g5.dtsi | 11 + + drivers/clk/clk-aspeed.c | 41 +- + drivers/media/platform/Kconfig | 9 + + drivers/media/platform/Makefile | 1 + + drivers/media/platform/aspeed-video.c | 1729 ++++++++++++++++++++ + include/dt-bindings/clock/aspeed-clock.h | 1 + + 8 files changed, 1824 insertions(+), 2 deletions(-) + create mode 100644 Documentation/devicetree/bindings/media/aspeed-video.txt + create mode 100644 drivers/media/platform/aspeed-video.c + +diff --git a/Documentation/devicetree/bindings/media/aspeed-video.txt b/Documentation/devicetree/bindings/media/aspeed-video.txt +new file mode 100644 +index 000000000000..78b464ae2672 +--- /dev/null ++++ b/Documentation/devicetree/bindings/media/aspeed-video.txt +@@ -0,0 +1,26 @@ ++* Device tree bindings for Aspeed Video Engine ++ ++The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs can ++capture and compress video data from digital or analog sources. ++ ++Required properties: ++ - compatible: "aspeed,ast2400-video-engine" or ++ "aspeed,ast2500-video-engine" ++ - reg: contains the offset and length of the VE memory region ++ - clocks: clock specifiers for the syscon clocks associated with ++ the VE (ordering must match the clock-names property) ++ - clock-names: "vclk" and "eclk" ++ - resets: reset specifier for the syscon reset associated with ++ the VE ++ - interrupts: the interrupt associated with the VE on this platform ++ ++Example: ++ ++video-engine@1e700000 { ++ compatible = "aspeed,ast2500-video-engine"; ++ reg = <0x1e700000 0x20000>; ++ clocks = <&syscon ASPEED_CLK_GATE_VCLK>, <&syscon ASPEED_CLK_GATE_ECLK>; ++ clock-names = "vclk", "eclk"; ++ resets = <&syscon ASPEED_RESET_VIDEO>; ++ interrupts = <7>; ++}; +diff --git a/MAINTAINERS b/MAINTAINERS +index 9e9b19ecf6f7..fd4fdb3e6474 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -2350,6 +2350,14 @@ S: Maintained + F: Documentation/hwmon/asc7621 + F: drivers/hwmon/asc7621.c + ++ASPEED VIDEO ENGINE DRIVER ++M: Eddie James <eajames@linux.ibm.com> ++L: linux-media@vger.kernel.org ++L: openbmc@lists.ozlabs.org (moderated for non-subscribers) ++S: Maintained ++F: drivers/media/platform/aspeed-video.c ++F: Documentation/devicetree/bindings/media/aspeed-video.txt ++ + ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS + M: Corentin Chary <corentin.chary@gmail.com> + L: acpi4asus-user@lists.sourceforge.net +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 0144d8bfa3fb..e55da933a70b 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -243,6 +243,17 @@ + interrupts = <0x19>; + }; + ++ video: video@1e700000 { ++ compatible = "aspeed,ast2500-video-engine"; ++ reg = <0x1e700000 0x20000>; ++ clocks = <&syscon ASPEED_CLK_GATE_VCLK>, ++ <&syscon ASPEED_CLK_GATE_ECLK>; ++ clock-names = "vclk", "eclk"; ++ resets = <&syscon ASPEED_RESET_VIDEO>; ++ interrupts = <7>; ++ status = "disabled"; ++ }; ++ + adc: adc@1e6e9000 { + compatible = "aspeed,ast2500-adc"; + reg = <0x1e6e9000 0xb0>; +diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c +index 61d41645e4fe..2429a2464556 100644 +--- a/drivers/clk/clk-aspeed.c ++++ b/drivers/clk/clk-aspeed.c +@@ -96,7 +96,7 @@ struct aspeed_clk_gate { + /* TODO: ask Aspeed about the actual parent data */ + static const struct aspeed_gate_data aspeed_gates[] = { + /* clk rst name parent flags */ +- [ASPEED_CLK_GATE_ECLK] = { 0, -1, "eclk-gate", "eclk", 0 }, /* Video Engine */ ++ [ASPEED_CLK_GATE_ECLK] = { 0, 6, "eclk-gate", "eclk", 0 }, /* Video Engine */ + [ASPEED_CLK_GATE_GCLK] = { 1, 7, "gclk-gate", NULL, 0 }, /* 2D engine */ + [ASPEED_CLK_GATE_MCLK] = { 2, -1, "mclk-gate", "mpll", CLK_IS_CRITICAL }, /* SDRAM */ + [ASPEED_CLK_GATE_VCLK] = { 3, 6, "vclk-gate", NULL, 0 }, /* Video Capture */ +@@ -122,6 +122,24 @@ static const struct aspeed_gate_data aspeed_gates[] = { + [ASPEED_CLK_GATE_LHCCLK] = { 28, -1, "lhclk-gate", "lhclk", 0 }, /* LPC master/LPC+ */ + }; + ++static const char * const eclk_parent_names[] = { ++ "mpll", ++ "hpll", ++ "dpll", ++}; ++ ++static const struct clk_div_table ast2500_eclk_div_table[] = { ++ { 0x0, 2 }, ++ { 0x1, 2 }, ++ { 0x2, 3 }, ++ { 0x3, 4 }, ++ { 0x4, 5 }, ++ { 0x5, 6 }, ++ { 0x6, 7 }, ++ { 0x7, 8 }, ++ { 0 } ++}; ++ + static const struct clk_div_table ast2500_mac_div_table[] = { + { 0x0, 4 }, /* Yep, really. Aspeed confirmed this is correct */ + { 0x1, 4 }, +@@ -201,18 +219,21 @@ static struct clk_hw *aspeed_ast2500_calc_pll(const char *name, u32 val) + + struct aspeed_clk_soc_data { + const struct clk_div_table *div_table; ++ const struct clk_div_table *eclk_div_table; + const struct clk_div_table *mac_div_table; + struct clk_hw *(*calc_pll)(const char *name, u32 val); + }; + + static const struct aspeed_clk_soc_data ast2500_data = { + .div_table = ast2500_div_table, ++ .eclk_div_table = ast2500_eclk_div_table, + .mac_div_table = ast2500_mac_div_table, + .calc_pll = aspeed_ast2500_calc_pll, + }; + + static const struct aspeed_clk_soc_data ast2400_data = { + .div_table = ast2400_div_table, ++ .eclk_div_table = ast2400_div_table, + .mac_div_table = ast2400_div_table, + .calc_pll = aspeed_ast2400_calc_pll, + }; +@@ -326,6 +347,7 @@ static const u8 aspeed_resets[] = { + [ASPEED_RESET_PECI] = 10, + [ASPEED_RESET_I2C] = 2, + [ASPEED_RESET_AHB] = 1, ++ [ASPEED_RESET_VIDEO] = 6, + + /* + * SCUD4 resets start at an offset to separate them from +@@ -548,6 +570,22 @@ static int aspeed_clk_probe(struct platform_device *pdev) + return PTR_ERR(hw); + aspeed_clk_data->hws[ASPEED_CLK_24M] = hw; + ++ hw = clk_hw_register_mux(dev, "eclk-mux", eclk_parent_names, ++ ARRAY_SIZE(eclk_parent_names), 0, ++ scu_base + ASPEED_CLK_SELECTION, 2, 0x3, 0, ++ &aspeed_clk_lock); ++ if (IS_ERR(hw)) ++ return PTR_ERR(hw); ++ aspeed_clk_data->hws[ASPEED_CLK_ECLK_MUX] = hw; ++ ++ hw = clk_hw_register_divider_table(dev, "eclk", "eclk-mux", 0, ++ scu_base + ASPEED_CLK_SELECTION, 28, ++ 3, 0, soc_data->eclk_div_table, ++ &aspeed_clk_lock); ++ if (IS_ERR(hw)) ++ return PTR_ERR(hw); ++ aspeed_clk_data->hws[ASPEED_CLK_ECLK] = hw; ++ + /* + * TODO: There are a number of clocks that not included in this driver + * as more information is required: +@@ -557,7 +595,6 @@ static int aspeed_clk_probe(struct platform_device *pdev) + * RGMII + * RMII + * UART[1..5] clock source mux +- * Video Engine (ECLK) mux and clock divider + */ + + for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) { +diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig +index 54fe90acb5b2..d6edf2d28f9b 100644 +--- a/drivers/media/platform/Kconfig ++++ b/drivers/media/platform/Kconfig +@@ -32,6 +32,15 @@ source "drivers/media/platform/davinci/Kconfig" + + source "drivers/media/platform/omap/Kconfig" + ++config VIDEO_ASPEED ++ tristate "Aspeed AST2400 and AST2500 Video Engine driver" ++ depends on VIDEO_V4L2 ++ select VIDEOBUF2_DMA_CONTIG ++ help ++ Support for the Aspeed Video Engine (VE) embedded in the Aspeed ++ AST2400 and AST2500 SOCs. The VE can capture and compress video data ++ from digital or analog sources. ++ + config VIDEO_SH_VOU + tristate "SuperH VOU video output driver" + depends on MEDIA_CAMERA_SUPPORT +diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile +index 41322ab65802..205c33a004fc 100644 +--- a/drivers/media/platform/Makefile ++++ b/drivers/media/platform/Makefile +@@ -3,6 +3,7 @@ + # Makefile for the video capture/playback device drivers. + # + ++obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o + obj-$(CONFIG_VIDEO_CADENCE) += cadence/ + obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o + obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ +diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c +new file mode 100644 +index 000000000000..dfec813f50a9 +--- /dev/null ++++ b/drivers/media/platform/aspeed-video.c +@@ -0,0 +1,1729 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++ ++#include <linux/atomic.h> ++#include <linux/bitfield.h> ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/dma-mapping.h> ++#include <linux/interrupt.h> ++#include <linux/jiffies.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/of.h> ++#include <linux/of_irq.h> ++#include <linux/of_reserved_mem.h> ++#include <linux/platform_device.h> ++#include <linux/reset.h> ++#include <linux/sched.h> ++#include <linux/spinlock.h> ++#include <linux/string.h> ++#include <linux/v4l2-controls.h> ++#include <linux/videodev2.h> ++#include <linux/wait.h> ++#include <linux/workqueue.h> ++#include <media/v4l2-ctrls.h> ++#include <media/v4l2-dev.h> ++#include <media/v4l2-device.h> ++#include <media/v4l2-dv-timings.h> ++#include <media/v4l2-event.h> ++#include <media/v4l2-ioctl.h> ++#include <media/videobuf2-dma-contig.h> ++ ++#define DEVICE_NAME "aspeed-video" ++ ++#define ASPEED_VIDEO_JPEG_NUM_QUALITIES 12 ++#define ASPEED_VIDEO_JPEG_HEADER_SIZE 10 ++#define ASPEED_VIDEO_JPEG_QUANT_SIZE 116 ++#define ASPEED_VIDEO_JPEG_DCT_SIZE 34 ++ ++#define MAX_FRAME_RATE 60 ++#define MAX_HEIGHT 1200 ++#define MAX_WIDTH 1920 ++#define MIN_HEIGHT 480 ++#define MIN_WIDTH 640 ++ ++#define NUM_POLARITY_CHECKS 10 ++#define INVALID_RESOLUTION_RETRIES 2 ++#define INVALID_RESOLUTION_DELAY msecs_to_jiffies(250) ++#define RESOLUTION_CHANGE_DELAY msecs_to_jiffies(500) ++#define MODE_DETECT_TIMEOUT msecs_to_jiffies(500) ++#define STOP_TIMEOUT msecs_to_jiffies(1000) ++#define DIRECT_FETCH_THRESHOLD 0x0c0000 /* 1024 * 768 */ ++ ++#define VE_MAX_SRC_BUFFER_SIZE 0x8ca000 /* 1920 * 1200, 32bpp */ ++#define VE_JPEG_HEADER_SIZE 0x006000 /* 512 * 12 * 4 */ ++ ++#define VE_PROTECTION_KEY 0x000 ++#define VE_PROTECTION_KEY_UNLOCK 0x1a038aa8 ++ ++#define VE_SEQ_CTRL 0x004 ++#define VE_SEQ_CTRL_TRIG_MODE_DET BIT(0) ++#define VE_SEQ_CTRL_TRIG_CAPTURE BIT(1) ++#define VE_SEQ_CTRL_FORCE_IDLE BIT(2) ++#define VE_SEQ_CTRL_MULT_FRAME BIT(3) ++#define VE_SEQ_CTRL_TRIG_COMP BIT(4) ++#define VE_SEQ_CTRL_AUTO_COMP BIT(5) ++#define VE_SEQ_CTRL_EN_WATCHDOG BIT(7) ++#define VE_SEQ_CTRL_YUV420 BIT(10) ++#define VE_SEQ_CTRL_COMP_FMT GENMASK(11, 10) ++#define VE_SEQ_CTRL_HALT BIT(12) ++#define VE_SEQ_CTRL_EN_WATCHDOG_COMP BIT(14) ++#define VE_SEQ_CTRL_TRIG_JPG BIT(15) ++#define VE_SEQ_CTRL_CAP_BUSY BIT(16) ++#define VE_SEQ_CTRL_COMP_BUSY BIT(18) ++ ++#ifdef CONFIG_MACH_ASPEED_G5 ++#define VE_SEQ_CTRL_JPEG_MODE BIT(13) /* AST2500 */ ++#else ++#define VE_SEQ_CTRL_JPEG_MODE BIT(8) /* AST2400 */ ++#endif /* CONFIG_MACH_ASPEED_G5 */ ++ ++#define VE_CTRL 0x008 ++#define VE_CTRL_HSYNC_POL BIT(0) ++#define VE_CTRL_VSYNC_POL BIT(1) ++#define VE_CTRL_SOURCE BIT(2) ++#define VE_CTRL_INT_DE BIT(4) ++#define VE_CTRL_DIRECT_FETCH BIT(5) ++#define VE_CTRL_YUV BIT(6) ++#define VE_CTRL_RGB BIT(7) ++#define VE_CTRL_CAPTURE_FMT GENMASK(7, 6) ++#define VE_CTRL_AUTO_OR_CURSOR BIT(8) ++#define VE_CTRL_CLK_INVERSE BIT(11) ++#define VE_CTRL_CLK_DELAY GENMASK(11, 9) ++#define VE_CTRL_INTERLACE BIT(14) ++#define VE_CTRL_HSYNC_POL_CTRL BIT(15) ++#define VE_CTRL_FRC GENMASK(23, 16) ++ ++#define VE_TGS_0 0x00c ++#define VE_TGS_1 0x010 ++#define VE_TGS_FIRST GENMASK(28, 16) ++#define VE_TGS_LAST GENMASK(12, 0) ++ ++#define VE_SCALING_FACTOR 0x014 ++#define VE_SCALING_FILTER0 0x018 ++#define VE_SCALING_FILTER1 0x01c ++#define VE_SCALING_FILTER2 0x020 ++#define VE_SCALING_FILTER3 0x024 ++ ++#define VE_CAP_WINDOW 0x030 ++#define VE_COMP_WINDOW 0x034 ++#define VE_COMP_PROC_OFFSET 0x038 ++#define VE_COMP_OFFSET 0x03c ++#define VE_JPEG_ADDR 0x040 ++#define VE_SRC0_ADDR 0x044 ++#define VE_SRC_SCANLINE_OFFSET 0x048 ++#define VE_SRC1_ADDR 0x04c ++#define VE_COMP_ADDR 0x054 ++ ++#define VE_STREAM_BUF_SIZE 0x058 ++#define VE_STREAM_BUF_SIZE_N_PACKETS GENMASK(5, 3) ++#define VE_STREAM_BUF_SIZE_P_SIZE GENMASK(2, 0) ++ ++#define VE_COMP_CTRL 0x060 ++#define VE_COMP_CTRL_VQ_DCT_ONLY BIT(0) ++#define VE_COMP_CTRL_VQ_4COLOR BIT(1) ++#define VE_COMP_CTRL_QUANTIZE BIT(2) ++#define VE_COMP_CTRL_EN_BQ BIT(4) ++#define VE_COMP_CTRL_EN_CRYPTO BIT(5) ++#define VE_COMP_CTRL_DCT_CHR GENMASK(10, 6) ++#define VE_COMP_CTRL_DCT_LUM GENMASK(15, 11) ++#define VE_COMP_CTRL_EN_HQ BIT(16) ++#define VE_COMP_CTRL_RSVD BIT(19) ++#define VE_COMP_CTRL_ENCODE GENMASK(21, 20) ++#define VE_COMP_CTRL_HQ_DCT_CHR GENMASK(26, 22) ++#define VE_COMP_CTRL_HQ_DCT_LUM GENMASK(31, 27) ++ ++#define VE_OFFSET_COMP_STREAM 0x078 ++ ++#define VE_SRC_LR_EDGE_DET 0x090 ++#define VE_SRC_LR_EDGE_DET_LEFT GENMASK(11, 0) ++#define VE_SRC_LR_EDGE_DET_NO_V BIT(12) ++#define VE_SRC_LR_EDGE_DET_NO_H BIT(13) ++#define VE_SRC_LR_EDGE_DET_NO_DISP BIT(14) ++#define VE_SRC_LR_EDGE_DET_NO_CLK BIT(15) ++#define VE_SRC_LR_EDGE_DET_RT_SHF 16 ++#define VE_SRC_LR_EDGE_DET_RT GENMASK(27, VE_SRC_LR_EDGE_DET_RT_SHF) ++#define VE_SRC_LR_EDGE_DET_INTERLACE BIT(31) ++ ++#define VE_SRC_TB_EDGE_DET 0x094 ++#define VE_SRC_TB_EDGE_DET_TOP GENMASK(12, 0) ++#define VE_SRC_TB_EDGE_DET_BOT_SHF 16 ++#define VE_SRC_TB_EDGE_DET_BOT GENMASK(28, VE_SRC_TB_EDGE_DET_BOT_SHF) ++ ++#define VE_MODE_DETECT_STATUS 0x098 ++#define VE_MODE_DETECT_H_PIXELS GENMASK(11, 0) ++#define VE_MODE_DETECT_V_LINES_SHF 16 ++#define VE_MODE_DETECT_V_LINES GENMASK(27, VE_MODE_DETECT_V_LINES_SHF) ++#define VE_MODE_DETECT_STATUS_VSYNC BIT(28) ++#define VE_MODE_DETECT_STATUS_HSYNC BIT(29) ++ ++#define VE_SYNC_STATUS 0x09c ++#define VE_SYNC_STATUS_HSYNC GENMASK(11, 0) ++#define VE_SYNC_STATUS_VSYNC_SHF 16 ++#define VE_SYNC_STATUS_VSYNC GENMASK(27, VE_SYNC_STATUS_VSYNC_SHF) ++ ++#define VE_INTERRUPT_CTRL 0x304 ++#define VE_INTERRUPT_STATUS 0x308 ++#define VE_INTERRUPT_MODE_DETECT_WD BIT(0) ++#define VE_INTERRUPT_CAPTURE_COMPLETE BIT(1) ++#define VE_INTERRUPT_COMP_READY BIT(2) ++#define VE_INTERRUPT_COMP_COMPLETE BIT(3) ++#define VE_INTERRUPT_MODE_DETECT BIT(4) ++#define VE_INTERRUPT_FRAME_COMPLETE BIT(5) ++#define VE_INTERRUPT_DECODE_ERR BIT(6) ++#define VE_INTERRUPT_HALT_READY BIT(8) ++#define VE_INTERRUPT_HANG_WD BIT(9) ++#define VE_INTERRUPT_STREAM_DESC BIT(10) ++#define VE_INTERRUPT_VSYNC_DESC BIT(11) ++ ++#define VE_MODE_DETECT 0x30c ++#define VE_MEM_RESTRICT_START 0x310 ++#define VE_MEM_RESTRICT_END 0x314 ++ ++enum { ++ VIDEO_MODE_DETECT_DONE, ++ VIDEO_RES_CHANGE, ++ VIDEO_RES_DETECT, ++ VIDEO_STREAMING, ++ VIDEO_FRAME_INPRG, ++ VIDEO_STOPPED, ++}; ++ ++struct aspeed_video_addr { ++ unsigned int size; ++ dma_addr_t dma; ++ void *virt; ++}; ++ ++struct aspeed_video_buffer { ++ struct vb2_v4l2_buffer vb; ++ struct list_head link; ++}; ++ ++#define to_aspeed_video_buffer(x) \ ++ container_of((x), struct aspeed_video_buffer, vb) ++ ++struct aspeed_video { ++ void __iomem *base; ++ struct clk *eclk; ++ struct clk *vclk; ++ struct reset_control *rst; ++ ++ struct device *dev; ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_device v4l2_dev; ++ struct v4l2_pix_format pix_fmt; ++ struct v4l2_bt_timings active_timings; ++ struct v4l2_bt_timings detected_timings; ++ u32 v4l2_input_status; ++ struct vb2_queue queue; ++ struct video_device vdev; ++ struct mutex video_lock; /* v4l2 and videobuf2 lock */ ++ ++ wait_queue_head_t wait; ++ spinlock_t lock; /* buffer list lock */ ++ struct delayed_work res_work; ++ struct list_head buffers; ++ unsigned long flags; ++ unsigned int sequence; ++ ++ unsigned int max_compressed_size; ++ struct aspeed_video_addr srcs[2]; ++ struct aspeed_video_addr jpeg; ++ ++ bool yuv420; ++ unsigned int frame_rate; ++ unsigned int jpeg_quality; ++ ++ unsigned int frame_bottom; ++ unsigned int frame_left; ++ unsigned int frame_right; ++ unsigned int frame_top; ++}; ++ ++#define to_aspeed_video(x) container_of((x), struct aspeed_video, v4l2_dev) ++ ++static const u32 aspeed_video_jpeg_header[ASPEED_VIDEO_JPEG_HEADER_SIZE] = { ++ 0xe0ffd8ff, 0x464a1000, 0x01004649, 0x60000101, 0x00006000, 0x0f00feff, ++ 0x00002d05, 0x00000000, 0x00000000, 0x00dbff00 ++}; ++ ++static const u32 aspeed_video_jpeg_quant[ASPEED_VIDEO_JPEG_QUANT_SIZE] = { ++ 0x081100c0, 0x00000000, 0x00110103, 0x03011102, 0xc4ff0111, 0x00001f00, ++ 0x01010501, 0x01010101, 0x00000000, 0x00000000, 0x04030201, 0x08070605, ++ 0xff0b0a09, 0x10b500c4, 0x03010200, 0x03040203, 0x04040505, 0x7d010000, ++ 0x00030201, 0x12051104, 0x06413121, 0x07615113, 0x32147122, 0x08a19181, ++ 0xc1b14223, 0xf0d15215, 0x72623324, 0x160a0982, 0x1a191817, 0x28272625, ++ 0x35342a29, 0x39383736, 0x4544433a, 0x49484746, 0x5554534a, 0x59585756, ++ 0x6564635a, 0x69686766, 0x7574736a, 0x79787776, 0x8584837a, 0x89888786, ++ 0x9493928a, 0x98979695, 0xa3a29a99, 0xa7a6a5a4, 0xb2aaa9a8, 0xb6b5b4b3, ++ 0xbab9b8b7, 0xc5c4c3c2, 0xc9c8c7c6, 0xd4d3d2ca, 0xd8d7d6d5, 0xe2e1dad9, ++ 0xe6e5e4e3, 0xeae9e8e7, 0xf4f3f2f1, 0xf8f7f6f5, 0xc4fffaf9, 0x00011f00, ++ 0x01010103, 0x01010101, 0x00000101, 0x00000000, 0x04030201, 0x08070605, ++ 0xff0b0a09, 0x11b500c4, 0x02010200, 0x04030404, 0x04040507, 0x77020100, ++ 0x03020100, 0x21050411, 0x41120631, 0x71610751, 0x81322213, 0x91421408, ++ 0x09c1b1a1, 0xf0523323, 0xd1726215, 0x3424160a, 0x17f125e1, 0x261a1918, ++ 0x2a292827, 0x38373635, 0x44433a39, 0x48474645, 0x54534a49, 0x58575655, ++ 0x64635a59, 0x68676665, 0x74736a69, 0x78777675, 0x83827a79, 0x87868584, ++ 0x928a8988, 0x96959493, 0x9a999897, 0xa5a4a3a2, 0xa9a8a7a6, 0xb4b3b2aa, ++ 0xb8b7b6b5, 0xc3c2bab9, 0xc7c6c5c4, 0xd2cac9c8, 0xd6d5d4d3, 0xdad9d8d7, ++ 0xe5e4e3e2, 0xe9e8e7e6, 0xf4f3f2ea, 0xf8f7f6f5, 0xdafffaf9, 0x01030c00, ++ 0x03110200, 0x003f0011 ++}; ++ ++static const u32 aspeed_video_jpeg_dct[ASPEED_VIDEO_JPEG_NUM_QUALITIES] ++ [ASPEED_VIDEO_JPEG_DCT_SIZE] = { ++ { 0x0d140043, 0x0c0f110f, 0x11101114, 0x17141516, 0x1e20321e, ++ 0x3d1e1b1b, 0x32242e2b, 0x4b4c3f48, 0x44463f47, 0x61735a50, ++ 0x566c5550, 0x88644644, 0x7a766c65, 0x4d808280, 0x8c978d60, ++ 0x7e73967d, 0xdbff7b80, 0x1f014300, 0x272d2121, 0x3030582d, ++ 0x697bb958, 0xb8b9b97b, 0xb9b8a6a6, 0xb9b9b9b9, 0xb9b9b9b9, ++ 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, ++ 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, 0xffb9b9b9 }, ++ { 0x0c110043, 0x0a0d0f0d, 0x0f0e0f11, 0x14111213, 0x1a1c2b1a, ++ 0x351a1818, 0x2b1f2826, 0x4142373f, 0x3c3d373e, 0x55644e46, ++ 0x4b5f4a46, 0x77573d3c, 0x6b675f58, 0x43707170, 0x7a847b54, ++ 0x6e64836d, 0xdbff6c70, 0x1b014300, 0x22271d1d, 0x2a2a4c27, ++ 0x5b6ba04c, 0xa0a0a06b, 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, ++ 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, ++ 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, 0xffa0a0a0 }, ++ { 0x090e0043, 0x090a0c0a, 0x0c0b0c0e, 0x110e0f10, 0x15172415, ++ 0x2c151313, 0x241a211f, 0x36372e34, 0x31322e33, 0x4653413a, ++ 0x3e4e3d3a, 0x62483231, 0x58564e49, 0x385d5e5d, 0x656d6645, ++ 0x5b536c5a, 0xdbff595d, 0x16014300, 0x1c201818, 0x22223f20, ++ 0x4b58853f, 0x85858558, 0x85858585, 0x85858585, 0x85858585, ++ 0x85858585, 0x85858585, 0x85858585, 0x85858585, 0x85858585, ++ 0x85858585, 0x85858585, 0x85858585, 0xff858585 }, ++ { 0x070b0043, 0x07080a08, 0x0a090a0b, 0x0d0b0c0c, 0x11121c11, ++ 0x23110f0f, 0x1c141a19, 0x2b2b2429, 0x27282428, 0x3842332e, ++ 0x313e302e, 0x4e392827, 0x46443e3a, 0x2c4a4a4a, 0x50565137, ++ 0x48425647, 0xdbff474a, 0x12014300, 0x161a1313, 0x1c1c331a, ++ 0x3d486c33, 0x6c6c6c48, 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, ++ 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, ++ 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, 0xff6c6c6c }, ++ { 0x06090043, 0x05060706, 0x07070709, 0x0a09090a, 0x0d0e160d, ++ 0x1b0d0c0c, 0x16101413, 0x21221c20, 0x1e1f1c20, 0x2b332824, ++ 0x26302624, 0x3d2d1f1e, 0x3735302d, 0x22393a39, 0x3f443f2b, ++ 0x38334338, 0xdbff3739, 0x0d014300, 0x11130e0e, 0x15152613, ++ 0x2d355026, 0x50505035, 0x50505050, 0x50505050, 0x50505050, ++ 0x50505050, 0x50505050, 0x50505050, 0x50505050, 0x50505050, ++ 0x50505050, 0x50505050, 0x50505050, 0xff505050 }, ++ { 0x04060043, 0x03040504, 0x05040506, 0x07060606, 0x09090f09, ++ 0x12090808, 0x0f0a0d0d, 0x16161315, 0x14151315, 0x1d221b18, ++ 0x19201918, 0x281e1514, 0x2423201e, 0x17262726, 0x2a2d2a1c, ++ 0x25222d25, 0xdbff2526, 0x09014300, 0x0b0d0a0a, 0x0e0e1a0d, ++ 0x1f25371a, 0x37373725, 0x37373737, 0x37373737, 0x37373737, ++ 0x37373737, 0x37373737, 0x37373737, 0x37373737, 0x37373737, ++ 0x37373737, 0x37373737, 0x37373737, 0xff373737 }, ++ { 0x02030043, 0x01020202, 0x02020203, 0x03030303, 0x04040704, ++ 0x09040404, 0x07050606, 0x0b0b090a, 0x0a0a090a, 0x0e110d0c, ++ 0x0c100c0c, 0x140f0a0a, 0x1211100f, 0x0b131313, 0x1516150e, ++ 0x12111612, 0xdbff1213, 0x04014300, 0x05060505, 0x07070d06, ++ 0x0f121b0d, 0x1b1b1b12, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, ++ 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, ++ 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, 0xff1b1b1b }, ++ { 0x01020043, 0x01010101, 0x01010102, 0x02020202, 0x03030503, ++ 0x06030202, 0x05030404, 0x07070607, 0x06070607, 0x090b0908, ++ 0x080a0808, 0x0d0a0706, 0x0c0b0a0a, 0x070c0d0c, 0x0e0f0e09, ++ 0x0c0b0f0c, 0xdbff0c0c, 0x03014300, 0x03040303, 0x04040804, ++ 0x0a0c1208, 0x1212120c, 0x12121212, 0x12121212, 0x12121212, ++ 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, ++ 0x12121212, 0x12121212, 0x12121212, 0xff121212 }, ++ { 0x01020043, 0x01010101, 0x01010102, 0x02020202, 0x03030503, ++ 0x06030202, 0x05030404, 0x07070607, 0x06070607, 0x090b0908, ++ 0x080a0808, 0x0d0a0706, 0x0c0b0a0a, 0x070c0d0c, 0x0e0f0e09, ++ 0x0c0b0f0c, 0xdbff0c0c, 0x02014300, 0x03030202, 0x04040703, ++ 0x080a0f07, 0x0f0f0f0a, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, ++ 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, ++ 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0xff0f0f0f }, ++ { 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x02020302, ++ 0x04020202, 0x03020303, 0x05050405, 0x05050405, 0x07080606, ++ 0x06080606, 0x0a070505, 0x09080807, 0x05090909, 0x0a0b0a07, ++ 0x09080b09, 0xdbff0909, 0x02014300, 0x02030202, 0x03030503, ++ 0x07080c05, 0x0c0c0c08, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, ++ 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, ++ 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0xff0c0c0c }, ++ { 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x01010201, ++ 0x03010101, 0x02010202, 0x03030303, 0x03030303, 0x04050404, ++ 0x04050404, 0x06050303, 0x06050505, 0x03060606, 0x07070704, ++ 0x06050706, 0xdbff0606, 0x01014300, 0x01020101, 0x02020402, ++ 0x05060904, 0x09090906, 0x09090909, 0x09090909, 0x09090909, ++ 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, ++ 0x09090909, 0x09090909, 0x09090909, 0xff090909 }, ++ { 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x01010101, ++ 0x01010101, 0x01010101, 0x01010101, 0x01010101, 0x02020202, ++ 0x02020202, 0x03020101, 0x03020202, 0x01030303, 0x03030302, ++ 0x03020303, 0xdbff0403, 0x01014300, 0x01010101, 0x01010201, ++ 0x03040602, 0x06060604, 0x06060606, 0x06060606, 0x06060606, ++ 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, ++ 0x06060606, 0x06060606, 0x06060606, 0xff060606 } ++}; ++ ++static const struct v4l2_dv_timings_cap aspeed_video_timings_cap = { ++ .type = V4L2_DV_BT_656_1120, ++ .bt = { ++ .min_width = MIN_WIDTH, ++ .max_width = MAX_WIDTH, ++ .min_height = MIN_HEIGHT, ++ .max_height = MAX_HEIGHT, ++ .min_pixelclock = 6574080, /* 640 x 480 x 24Hz */ ++ .max_pixelclock = 138240000, /* 1920 x 1200 x 60Hz */ ++ .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | ++ V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, ++ .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | ++ V4L2_DV_BT_CAP_REDUCED_BLANKING | ++ V4L2_DV_BT_CAP_CUSTOM, ++ }, ++}; ++ ++static void aspeed_video_init_jpeg_table(u32 *table, bool yuv420) ++{ ++ int i; ++ unsigned int base; ++ ++ for (i = 0; i < ASPEED_VIDEO_JPEG_NUM_QUALITIES; i++) { ++ base = 256 * i; /* AST HW requires this header spacing */ ++ memcpy(&table[base], aspeed_video_jpeg_header, ++ sizeof(aspeed_video_jpeg_header)); ++ ++ base += ASPEED_VIDEO_JPEG_HEADER_SIZE; ++ memcpy(&table[base], aspeed_video_jpeg_dct[i], ++ sizeof(aspeed_video_jpeg_dct[i])); ++ ++ base += ASPEED_VIDEO_JPEG_DCT_SIZE; ++ memcpy(&table[base], aspeed_video_jpeg_quant, ++ sizeof(aspeed_video_jpeg_quant)); ++ ++ if (yuv420) ++ table[base + 2] = 0x00220103; ++ } ++} ++ ++static void aspeed_video_update(struct aspeed_video *video, u32 reg, u32 clear, ++ u32 bits) ++{ ++ u32 t = readl(video->base + reg); ++ u32 before = t; ++ ++ t &= ~clear; ++ t |= bits; ++ writel(t, video->base + reg); ++ dev_dbg(video->dev, "update %03x[%08x -> %08x]\n", reg, before, ++ readl(video->base + reg)); ++} ++ ++static u32 aspeed_video_read(struct aspeed_video *video, u32 reg) ++{ ++ u32 t = readl(video->base + reg); ++ ++ dev_dbg(video->dev, "read %03x[%08x]\n", reg, t); ++ return t; ++} ++ ++static void aspeed_video_write(struct aspeed_video *video, u32 reg, u32 val) ++{ ++ writel(val, video->base + reg); ++ dev_dbg(video->dev, "write %03x[%08x]\n", reg, ++ readl(video->base + reg)); ++} ++ ++static int aspeed_video_start_frame(struct aspeed_video *video) ++{ ++ dma_addr_t addr; ++ unsigned long flags; ++ struct aspeed_video_buffer *buf; ++ u32 seq_ctrl = aspeed_video_read(video, VE_SEQ_CTRL); ++ ++ if (video->v4l2_input_status) { ++ dev_dbg(video->dev, "No signal; don't start frame\n"); ++ return 0; ++ } ++ ++ if (!(seq_ctrl & VE_SEQ_CTRL_COMP_BUSY) || ++ !(seq_ctrl & VE_SEQ_CTRL_CAP_BUSY)) { ++ dev_err(video->dev, "Engine busy; don't start frame\n"); ++ return -EBUSY; ++ } ++ ++ spin_lock_irqsave(&video->lock, flags); ++ buf = list_first_entry_or_null(&video->buffers, ++ struct aspeed_video_buffer, link); ++ if (!buf) { ++ spin_unlock_irqrestore(&video->lock, flags); ++ dev_dbg(video->dev, "No buffers; don't start frame\n"); ++ return -EPROTO; ++ } ++ ++ set_bit(VIDEO_FRAME_INPRG, &video->flags); ++ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); ++ spin_unlock_irqrestore(&video->lock, flags); ++ ++ aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0); ++ aspeed_video_write(video, VE_COMP_OFFSET, 0); ++ aspeed_video_write(video, VE_COMP_ADDR, addr); ++ ++ aspeed_video_update(video, VE_INTERRUPT_CTRL, 0, ++ VE_INTERRUPT_COMP_COMPLETE | ++ VE_INTERRUPT_CAPTURE_COMPLETE); ++ ++ aspeed_video_update(video, VE_SEQ_CTRL, 0, ++ VE_SEQ_CTRL_TRIG_CAPTURE | VE_SEQ_CTRL_TRIG_COMP); ++ ++ return 0; ++} ++ ++static void aspeed_video_enable_mode_detect(struct aspeed_video *video) ++{ ++ /* Enable mode detect interrupts */ ++ aspeed_video_update(video, VE_INTERRUPT_CTRL, 0, ++ VE_INTERRUPT_MODE_DETECT); ++ ++ /* Trigger mode detect */ ++ aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_TRIG_MODE_DET); ++} ++ ++static void aspeed_video_reset(struct aspeed_video *video) ++{ ++ /* Reset the engine */ ++ reset_control_assert(video->rst); ++ ++ /* Don't usleep here; function may be called in interrupt context */ ++ udelay(100); ++ reset_control_deassert(video->rst); ++} ++ ++static void aspeed_video_off(struct aspeed_video *video) ++{ ++ aspeed_video_reset(video); ++ ++ /* Turn off the relevant clocks */ ++ clk_disable_unprepare(video->vclk); ++ clk_disable_unprepare(video->eclk); ++} ++ ++static void aspeed_video_on(struct aspeed_video *video) ++{ ++ /* Turn on the relevant clocks */ ++ clk_prepare_enable(video->eclk); ++ clk_prepare_enable(video->vclk); ++ ++ aspeed_video_reset(video); ++} ++ ++static void aspeed_video_bufs_done(struct aspeed_video *video, ++ enum vb2_buffer_state state) ++{ ++ unsigned long flags; ++ struct aspeed_video_buffer *buf; ++ ++ spin_lock_irqsave(&video->lock, flags); ++ list_for_each_entry(buf, &video->buffers, link) ++ vb2_buffer_done(&buf->vb.vb2_buf, state); ++ INIT_LIST_HEAD(&video->buffers); ++ spin_unlock_irqrestore(&video->lock, flags); ++} ++ ++static void aspeed_video_irq_res_change(struct aspeed_video *video) ++{ ++ dev_dbg(video->dev, "Resolution changed; resetting\n"); ++ ++ set_bit(VIDEO_RES_CHANGE, &video->flags); ++ clear_bit(VIDEO_FRAME_INPRG, &video->flags); ++ ++ aspeed_video_off(video); ++ aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR); ++ ++ schedule_delayed_work(&video->res_work, RESOLUTION_CHANGE_DELAY); ++} ++ ++static irqreturn_t aspeed_video_irq(int irq, void *arg) ++{ ++ struct aspeed_video *video = arg; ++ u32 sts = aspeed_video_read(video, VE_INTERRUPT_STATUS); ++ ++ /* ++ * Resolution changed or signal was lost; reset the engine and ++ * re-initialize ++ */ ++ if (sts & VE_INTERRUPT_MODE_DETECT_WD) { ++ aspeed_video_irq_res_change(video); ++ return IRQ_HANDLED; ++ } ++ ++ if (sts & VE_INTERRUPT_MODE_DETECT) { ++ if (test_bit(VIDEO_RES_DETECT, &video->flags)) { ++ aspeed_video_update(video, VE_INTERRUPT_CTRL, ++ VE_INTERRUPT_MODE_DETECT, 0); ++ aspeed_video_write(video, VE_INTERRUPT_STATUS, ++ VE_INTERRUPT_MODE_DETECT); ++ ++ set_bit(VIDEO_MODE_DETECT_DONE, &video->flags); ++ wake_up_interruptible_all(&video->wait); ++ } else { ++ /* ++ * Signal acquired while NOT doing resolution ++ * detection; reset the engine and re-initialize ++ */ ++ aspeed_video_irq_res_change(video); ++ return IRQ_HANDLED; ++ } ++ } ++ ++ if ((sts & VE_INTERRUPT_COMP_COMPLETE) && ++ (sts & VE_INTERRUPT_CAPTURE_COMPLETE)) { ++ struct aspeed_video_buffer *buf; ++ u32 frame_size = aspeed_video_read(video, ++ VE_OFFSET_COMP_STREAM); ++ ++ spin_lock(&video->lock); ++ clear_bit(VIDEO_FRAME_INPRG, &video->flags); ++ buf = list_first_entry_or_null(&video->buffers, ++ struct aspeed_video_buffer, ++ link); ++ if (buf) { ++ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, frame_size); ++ ++ if (!list_is_last(&buf->link, &video->buffers)) { ++ buf->vb.vb2_buf.timestamp = ktime_get_ns(); ++ buf->vb.sequence = video->sequence++; ++ buf->vb.field = V4L2_FIELD_NONE; ++ vb2_buffer_done(&buf->vb.vb2_buf, ++ VB2_BUF_STATE_DONE); ++ list_del(&buf->link); ++ } ++ } ++ spin_unlock(&video->lock); ++ ++ aspeed_video_update(video, VE_SEQ_CTRL, ++ VE_SEQ_CTRL_TRIG_CAPTURE | ++ VE_SEQ_CTRL_FORCE_IDLE | ++ VE_SEQ_CTRL_TRIG_COMP, 0); ++ aspeed_video_update(video, VE_INTERRUPT_CTRL, ++ VE_INTERRUPT_COMP_COMPLETE | ++ VE_INTERRUPT_CAPTURE_COMPLETE, 0); ++ aspeed_video_write(video, VE_INTERRUPT_STATUS, ++ VE_INTERRUPT_COMP_COMPLETE | ++ VE_INTERRUPT_CAPTURE_COMPLETE); ++ ++ if (test_bit(VIDEO_STREAMING, &video->flags) && buf) ++ aspeed_video_start_frame(video); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void aspeed_video_check_and_set_polarity(struct aspeed_video *video) ++{ ++ int i; ++ int hsync_counter = 0; ++ int vsync_counter = 0; ++ u32 sts; ++ ++ for (i = 0; i < NUM_POLARITY_CHECKS; ++i) { ++ sts = aspeed_video_read(video, VE_MODE_DETECT_STATUS); ++ if (sts & VE_MODE_DETECT_STATUS_VSYNC) ++ vsync_counter--; ++ else ++ vsync_counter++; ++ ++ if (sts & VE_MODE_DETECT_STATUS_HSYNC) ++ hsync_counter--; ++ else ++ hsync_counter++; ++ } ++ ++ if (hsync_counter < 0 || vsync_counter < 0) { ++ u32 ctrl; ++ ++ if (hsync_counter < 0) { ++ ctrl = VE_CTRL_HSYNC_POL; ++ video->detected_timings.polarities &= ++ ~V4L2_DV_HSYNC_POS_POL; ++ } else { ++ video->detected_timings.polarities |= ++ V4L2_DV_HSYNC_POS_POL; ++ } ++ ++ if (vsync_counter < 0) { ++ ctrl = VE_CTRL_VSYNC_POL; ++ video->detected_timings.polarities &= ++ ~V4L2_DV_VSYNC_POS_POL; ++ } else { ++ video->detected_timings.polarities |= ++ V4L2_DV_VSYNC_POS_POL; ++ } ++ ++ aspeed_video_update(video, VE_CTRL, 0, ctrl); ++ } ++} ++ ++static bool aspeed_video_alloc_buf(struct aspeed_video *video, ++ struct aspeed_video_addr *addr, ++ unsigned int size) ++{ ++ addr->virt = dma_alloc_coherent(video->dev, size, &addr->dma, ++ GFP_KERNEL); ++ if (!addr->virt) ++ return false; ++ ++ addr->size = size; ++ return true; ++} ++ ++static void aspeed_video_free_buf(struct aspeed_video *video, ++ struct aspeed_video_addr *addr) ++{ ++ dma_free_coherent(video->dev, addr->size, addr->virt, addr->dma); ++ addr->size = 0; ++ addr->dma = 0ULL; ++ addr->virt = NULL; ++} ++ ++/* ++ * Get the minimum HW-supported compression buffer size for the frame size. ++ * Assume worst-case JPEG compression size is 1/8 raw size. This should be ++ * plenty even for maximum quality; any worse and the engine will simply return ++ * incomplete JPEGs. ++ */ ++static void aspeed_video_calc_compressed_size(struct aspeed_video *video, ++ unsigned int frame_size) ++{ ++ int i, j; ++ u32 compression_buffer_size_reg = 0; ++ unsigned int size; ++ const unsigned int num_compression_packets = 4; ++ const unsigned int compression_packet_size = 1024; ++ const unsigned int max_compressed_size = frame_size / 2; /* 4bpp / 8 */ ++ ++ video->max_compressed_size = UINT_MAX; ++ ++ for (i = 0; i < 6; ++i) { ++ for (j = 0; j < 8; ++j) { ++ size = (num_compression_packets << i) * ++ (compression_packet_size << j); ++ if (size < max_compressed_size) ++ continue; ++ ++ if (size < video->max_compressed_size) { ++ compression_buffer_size_reg = (i << 3) | j; ++ video->max_compressed_size = size; ++ } ++ } ++ } ++ ++ aspeed_video_write(video, VE_STREAM_BUF_SIZE, ++ compression_buffer_size_reg); ++ ++ dev_dbg(video->dev, "Max compressed size: %x\n", ++ video->max_compressed_size); ++} ++ ++#define res_check(v) test_and_clear_bit(VIDEO_MODE_DETECT_DONE, &(v)->flags) ++ ++static void aspeed_video_get_resolution(struct aspeed_video *video) ++{ ++ bool invalid_resolution = true; ++ int rc; ++ int tries = 0; ++ u32 mds; ++ u32 src_lr_edge; ++ u32 src_tb_edge; ++ u32 sync; ++ struct v4l2_bt_timings *det = &video->detected_timings; ++ ++ det->width = MIN_WIDTH; ++ det->height = MIN_HEIGHT; ++ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; ++ ++ /* ++ * Since we need max buffer size for detection, free the second source ++ * buffer first. ++ */ ++ if (video->srcs[1].size) ++ aspeed_video_free_buf(video, &video->srcs[1]); ++ ++ if (video->srcs[0].size < VE_MAX_SRC_BUFFER_SIZE) { ++ if (video->srcs[0].size) ++ aspeed_video_free_buf(video, &video->srcs[0]); ++ ++ if (!aspeed_video_alloc_buf(video, &video->srcs[0], ++ VE_MAX_SRC_BUFFER_SIZE)) { ++ dev_err(video->dev, ++ "Failed to allocate source buffers\n"); ++ return; ++ } ++ } ++ ++ aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma); ++ ++ do { ++ if (tries) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (schedule_timeout(INVALID_RESOLUTION_DELAY)) ++ return; ++ } ++ ++ set_bit(VIDEO_RES_DETECT, &video->flags); ++ aspeed_video_enable_mode_detect(video); ++ ++ rc = wait_event_interruptible_timeout(video->wait, ++ res_check(video), ++ MODE_DETECT_TIMEOUT); ++ if (!rc) { ++ dev_err(video->dev, "Timed out; first mode detect\n"); ++ clear_bit(VIDEO_RES_DETECT, &video->flags); ++ return; ++ } ++ ++ /* Disable mode detect in order to re-trigger */ ++ aspeed_video_update(video, VE_SEQ_CTRL, ++ VE_SEQ_CTRL_TRIG_MODE_DET, 0); ++ ++ aspeed_video_check_and_set_polarity(video); ++ ++ aspeed_video_enable_mode_detect(video); ++ ++ rc = wait_event_interruptible_timeout(video->wait, ++ res_check(video), ++ MODE_DETECT_TIMEOUT); ++ clear_bit(VIDEO_RES_DETECT, &video->flags); ++ if (!rc) { ++ dev_err(video->dev, "Timed out; second mode detect\n"); ++ return; ++ } ++ ++ src_lr_edge = aspeed_video_read(video, VE_SRC_LR_EDGE_DET); ++ src_tb_edge = aspeed_video_read(video, VE_SRC_TB_EDGE_DET); ++ mds = aspeed_video_read(video, VE_MODE_DETECT_STATUS); ++ sync = aspeed_video_read(video, VE_SYNC_STATUS); ++ ++ video->frame_bottom = (src_tb_edge & VE_SRC_TB_EDGE_DET_BOT) >> ++ VE_SRC_TB_EDGE_DET_BOT_SHF; ++ video->frame_top = src_tb_edge & VE_SRC_TB_EDGE_DET_TOP; ++ det->vfrontporch = video->frame_top; ++ det->vbackporch = ((mds & VE_MODE_DETECT_V_LINES) >> ++ VE_MODE_DETECT_V_LINES_SHF) - video->frame_bottom; ++ det->vsync = (sync & VE_SYNC_STATUS_VSYNC) >> ++ VE_SYNC_STATUS_VSYNC_SHF; ++ if (video->frame_top > video->frame_bottom) ++ continue; ++ ++ video->frame_right = (src_lr_edge & VE_SRC_LR_EDGE_DET_RT) >> ++ VE_SRC_LR_EDGE_DET_RT_SHF; ++ video->frame_left = src_lr_edge & VE_SRC_LR_EDGE_DET_LEFT; ++ det->hfrontporch = video->frame_left; ++ det->hbackporch = (mds & VE_MODE_DETECT_H_PIXELS) - ++ video->frame_right; ++ det->hsync = sync & VE_SYNC_STATUS_HSYNC; ++ if (video->frame_left > video->frame_right) ++ continue; ++ ++ invalid_resolution = false; ++ } while (invalid_resolution && (tries++ < INVALID_RESOLUTION_RETRIES)); ++ ++ if (invalid_resolution) { ++ dev_err(video->dev, "Invalid resolution detected\n"); ++ return; ++ } ++ ++ det->height = (video->frame_bottom - video->frame_top) + 1; ++ det->width = (video->frame_right - video->frame_left) + 1; ++ video->v4l2_input_status = 0; ++ ++ /* ++ * Enable mode-detect watchdog, resolution-change watchdog and ++ * automatic compression after frame capture. ++ */ ++ aspeed_video_update(video, VE_INTERRUPT_CTRL, 0, ++ VE_INTERRUPT_MODE_DETECT_WD); ++ aspeed_video_update(video, VE_SEQ_CTRL, 0, ++ VE_SEQ_CTRL_AUTO_COMP | VE_SEQ_CTRL_EN_WATCHDOG); ++ ++ dev_dbg(video->dev, "Got resolution: %dx%d\n", det->width, ++ det->height); ++} ++ ++static void aspeed_video_set_resolution(struct aspeed_video *video) ++{ ++ struct v4l2_bt_timings *act = &video->active_timings; ++ unsigned int size = act->width * act->height; ++ ++ aspeed_video_calc_compressed_size(video, size); ++ ++ /* Don't use direct mode below 1024 x 768 (irqs don't fire) */ ++ if (size < DIRECT_FETCH_THRESHOLD) { ++ aspeed_video_write(video, VE_TGS_0, ++ FIELD_PREP(VE_TGS_FIRST, ++ video->frame_left - 1) | ++ FIELD_PREP(VE_TGS_LAST, ++ video->frame_right)); ++ aspeed_video_write(video, VE_TGS_1, ++ FIELD_PREP(VE_TGS_FIRST, video->frame_top) | ++ FIELD_PREP(VE_TGS_LAST, ++ video->frame_bottom + 1)); ++ aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_INT_DE); ++ } else { ++ aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_DIRECT_FETCH); ++ } ++ ++ /* Set capture/compression frame sizes */ ++ aspeed_video_write(video, VE_CAP_WINDOW, ++ act->width << 16 | act->height); ++ aspeed_video_write(video, VE_COMP_WINDOW, ++ act->width << 16 | act->height); ++ aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET, act->width * 4); ++ ++ size *= 4; ++ ++ if (size == video->srcs[0].size / 2) { ++ aspeed_video_write(video, VE_SRC1_ADDR, ++ video->srcs[0].dma + size); ++ } else if (size == video->srcs[0].size) { ++ if (!aspeed_video_alloc_buf(video, &video->srcs[1], size)) ++ goto err_mem; ++ ++ aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma); ++ } else { ++ aspeed_video_free_buf(video, &video->srcs[0]); ++ ++ if (!aspeed_video_alloc_buf(video, &video->srcs[0], size)) ++ goto err_mem; ++ ++ if (!aspeed_video_alloc_buf(video, &video->srcs[1], size)) ++ goto err_mem; ++ ++ aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma); ++ aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma); ++ } ++ ++ return; ++ ++err_mem: ++ dev_err(video->dev, "Failed to allocate source buffers\n"); ++ ++ if (video->srcs[0].size) ++ aspeed_video_free_buf(video, &video->srcs[0]); ++} ++ ++static void aspeed_video_init_regs(struct aspeed_video *video) ++{ ++ u32 comp_ctrl = VE_COMP_CTRL_RSVD | ++ FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) | ++ FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10); ++ u32 ctrl = VE_CTRL_AUTO_OR_CURSOR; ++ u32 seq_ctrl = VE_SEQ_CTRL_JPEG_MODE; ++ ++ if (video->frame_rate) ++ ctrl |= FIELD_PREP(VE_CTRL_FRC, video->frame_rate); ++ ++ if (video->yuv420) ++ seq_ctrl |= VE_SEQ_CTRL_YUV420; ++ ++ /* Unlock VE registers */ ++ aspeed_video_write(video, VE_PROTECTION_KEY, VE_PROTECTION_KEY_UNLOCK); ++ ++ /* Disable interrupts */ ++ aspeed_video_write(video, VE_INTERRUPT_CTRL, 0); ++ aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xffffffff); ++ ++ /* Clear the offset */ ++ aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0); ++ aspeed_video_write(video, VE_COMP_OFFSET, 0); ++ ++ aspeed_video_write(video, VE_JPEG_ADDR, video->jpeg.dma); ++ ++ /* Set control registers */ ++ aspeed_video_write(video, VE_SEQ_CTRL, seq_ctrl); ++ aspeed_video_write(video, VE_CTRL, ctrl); ++ aspeed_video_write(video, VE_COMP_CTRL, comp_ctrl); ++ ++ /* Don't downscale */ ++ aspeed_video_write(video, VE_SCALING_FACTOR, 0x10001000); ++ aspeed_video_write(video, VE_SCALING_FILTER0, 0x00200000); ++ aspeed_video_write(video, VE_SCALING_FILTER1, 0x00200000); ++ aspeed_video_write(video, VE_SCALING_FILTER2, 0x00200000); ++ aspeed_video_write(video, VE_SCALING_FILTER3, 0x00200000); ++ ++ /* Set mode detection defaults */ ++ aspeed_video_write(video, VE_MODE_DETECT, 0x22666500); ++} ++ ++static void aspeed_video_start(struct aspeed_video *video) ++{ ++ aspeed_video_on(video); ++ ++ aspeed_video_init_regs(video); ++ ++ /* Resolution set to 640x480 if no signal found */ ++ aspeed_video_get_resolution(video); ++ ++ /* Set timings since the device is being opened for the first time */ ++ video->active_timings = video->detected_timings; ++ aspeed_video_set_resolution(video); ++ ++ video->pix_fmt.width = video->active_timings.width; ++ video->pix_fmt.height = video->active_timings.height; ++ video->pix_fmt.sizeimage = video->max_compressed_size; ++} ++ ++static void aspeed_video_stop(struct aspeed_video *video) ++{ ++ set_bit(VIDEO_STOPPED, &video->flags); ++ cancel_delayed_work_sync(&video->res_work); ++ ++ aspeed_video_off(video); ++ ++ if (video->srcs[0].size) ++ aspeed_video_free_buf(video, &video->srcs[0]); ++ ++ if (video->srcs[1].size) ++ aspeed_video_free_buf(video, &video->srcs[1]); ++ ++ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; ++ video->flags = 0; ++} ++ ++static int aspeed_video_querycap(struct file *file, void *fh, ++ struct v4l2_capability *cap) ++{ ++ strscpy(cap->driver, DEVICE_NAME, sizeof(cap->driver)); ++ strscpy(cap->card, "Aspeed Video Engine", sizeof(cap->card)); ++ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", ++ DEVICE_NAME); ++ ++ return 0; ++} ++ ++static int aspeed_video_enum_format(struct file *file, void *fh, ++ struct v4l2_fmtdesc *f) ++{ ++ if (f->index) ++ return -EINVAL; ++ ++ f->pixelformat = V4L2_PIX_FMT_JPEG; ++ ++ return 0; ++} ++ ++static int aspeed_video_get_format(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ f->fmt.pix = video->pix_fmt; ++ ++ return 0; ++} ++ ++static int aspeed_video_enum_input(struct file *file, void *fh, ++ struct v4l2_input *inp) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ if (inp->index) ++ return -EINVAL; ++ ++ strscpy(inp->name, "Host VGA capture", sizeof(inp->name)); ++ inp->type = V4L2_INPUT_TYPE_CAMERA; ++ inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; ++ inp->status = video->v4l2_input_status; ++ ++ return 0; ++} ++ ++static int aspeed_video_get_input(struct file *file, void *fh, unsigned int *i) ++{ ++ *i = 0; ++ ++ return 0; ++} ++ ++static int aspeed_video_set_input(struct file *file, void *fh, unsigned int i) ++{ ++ if (i) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int aspeed_video_get_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *a) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ a->parm.capture.readbuffers = 3; ++ a->parm.capture.timeperframe.numerator = 1; ++ if (!video->frame_rate) ++ a->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; ++ else ++ a->parm.capture.timeperframe.denominator = video->frame_rate; ++ ++ return 0; ++} ++ ++static int aspeed_video_set_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *a) ++{ ++ unsigned int frame_rate = 0; ++ struct aspeed_video *video = video_drvdata(file); ++ ++ a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ a->parm.capture.readbuffers = 3; ++ ++ if (a->parm.capture.timeperframe.numerator) ++ frame_rate = a->parm.capture.timeperframe.denominator / ++ a->parm.capture.timeperframe.numerator; ++ ++ if (!frame_rate || frame_rate > MAX_FRAME_RATE) { ++ frame_rate = 0; ++ a->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; ++ a->parm.capture.timeperframe.numerator = 1; ++ } ++ ++ if (video->frame_rate != frame_rate) { ++ video->frame_rate = frame_rate; ++ aspeed_video_update(video, VE_CTRL, VE_CTRL_FRC, ++ FIELD_PREP(VE_CTRL_FRC, frame_rate)); ++ } ++ ++ return 0; ++} ++ ++static int aspeed_video_enum_framesizes(struct file *file, void *fh, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ if (fsize->index) ++ return -EINVAL; ++ ++ if (fsize->pixel_format != V4L2_PIX_FMT_JPEG) ++ return -EINVAL; ++ ++ fsize->discrete.width = video->pix_fmt.width; ++ fsize->discrete.height = video->pix_fmt.height; ++ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; ++ ++ return 0; ++} ++ ++static int aspeed_video_enum_frameintervals(struct file *file, void *fh, ++ struct v4l2_frmivalenum *fival) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ if (fival->index) ++ return -EINVAL; ++ ++ if (fival->width != video->detected_timings.width || ++ fival->height != video->detected_timings.height) ++ return -EINVAL; ++ ++ if (fival->pixel_format != V4L2_PIX_FMT_JPEG) ++ return -EINVAL; ++ ++ fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; ++ ++ fival->stepwise.min.denominator = MAX_FRAME_RATE; ++ fival->stepwise.min.numerator = 1; ++ fival->stepwise.max.denominator = 1; ++ fival->stepwise.max.numerator = 1; ++ fival->stepwise.step = fival->stepwise.max; ++ ++ return 0; ++} ++ ++static int aspeed_video_set_dv_timings(struct file *file, void *fh, ++ struct v4l2_dv_timings *timings) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ if (timings->bt.width == video->active_timings.width && ++ timings->bt.height == video->active_timings.height) ++ return 0; ++ ++ if (vb2_is_busy(&video->queue)) ++ return -EBUSY; ++ ++ video->active_timings = timings->bt; ++ ++ aspeed_video_set_resolution(video); ++ ++ video->pix_fmt.width = timings->bt.width; ++ video->pix_fmt.height = timings->bt.height; ++ video->pix_fmt.sizeimage = video->max_compressed_size; ++ ++ timings->type = V4L2_DV_BT_656_1120; ++ ++ return 0; ++} ++ ++static int aspeed_video_get_dv_timings(struct file *file, void *fh, ++ struct v4l2_dv_timings *timings) ++{ ++ struct aspeed_video *video = video_drvdata(file); ++ ++ timings->type = V4L2_DV_BT_656_1120; ++ timings->bt = video->active_timings; ++ ++ return 0; ++} ++ ++static int aspeed_video_query_dv_timings(struct file *file, void *fh, ++ struct v4l2_dv_timings *timings) ++{ ++ int rc; ++ struct aspeed_video *video = video_drvdata(file); ++ ++ /* ++ * This blocks only if the driver is currently in the process of ++ * detecting a new resolution; in the event of no signal or timeout ++ * this function is woken up. ++ */ ++ if (file->f_flags & O_NONBLOCK) { ++ if (test_bit(VIDEO_RES_CHANGE, &video->flags)) ++ return -EAGAIN; ++ } else { ++ rc = wait_event_interruptible(video->wait, ++ !test_bit(VIDEO_RES_CHANGE, ++ &video->flags)); ++ if (rc) ++ return -EINTR; ++ } ++ ++ timings->type = V4L2_DV_BT_656_1120; ++ timings->bt = video->detected_timings; ++ ++ return video->v4l2_input_status ? -ENOLINK : 0; ++} ++ ++static int aspeed_video_enum_dv_timings(struct file *file, void *fh, ++ struct v4l2_enum_dv_timings *timings) ++{ ++ return v4l2_enum_dv_timings_cap(timings, &aspeed_video_timings_cap, ++ NULL, NULL); ++} ++ ++static int aspeed_video_dv_timings_cap(struct file *file, void *fh, ++ struct v4l2_dv_timings_cap *cap) ++{ ++ *cap = aspeed_video_timings_cap; ++ ++ return 0; ++} ++ ++static int aspeed_video_sub_event(struct v4l2_fh *fh, ++ const struct v4l2_event_subscription *sub) ++{ ++ switch (sub->type) { ++ case V4L2_EVENT_SOURCE_CHANGE: ++ return v4l2_src_change_event_subscribe(fh, sub); ++ } ++ ++ return v4l2_ctrl_subscribe_event(fh, sub); ++} ++ ++static const struct v4l2_ioctl_ops aspeed_video_ioctl_ops = { ++ .vidioc_querycap = aspeed_video_querycap, ++ ++ .vidioc_enum_fmt_vid_cap = aspeed_video_enum_format, ++ .vidioc_g_fmt_vid_cap = aspeed_video_get_format, ++ .vidioc_s_fmt_vid_cap = aspeed_video_get_format, ++ .vidioc_try_fmt_vid_cap = aspeed_video_get_format, ++ ++ .vidioc_reqbufs = vb2_ioctl_reqbufs, ++ .vidioc_querybuf = vb2_ioctl_querybuf, ++ .vidioc_qbuf = vb2_ioctl_qbuf, ++ .vidioc_expbuf = vb2_ioctl_expbuf, ++ .vidioc_dqbuf = vb2_ioctl_dqbuf, ++ .vidioc_create_bufs = vb2_ioctl_create_bufs, ++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, ++ .vidioc_streamon = vb2_ioctl_streamon, ++ .vidioc_streamoff = vb2_ioctl_streamoff, ++ ++ .vidioc_enum_input = aspeed_video_enum_input, ++ .vidioc_g_input = aspeed_video_get_input, ++ .vidioc_s_input = aspeed_video_set_input, ++ ++ .vidioc_g_parm = aspeed_video_get_parm, ++ .vidioc_s_parm = aspeed_video_set_parm, ++ .vidioc_enum_framesizes = aspeed_video_enum_framesizes, ++ .vidioc_enum_frameintervals = aspeed_video_enum_frameintervals, ++ ++ .vidioc_s_dv_timings = aspeed_video_set_dv_timings, ++ .vidioc_g_dv_timings = aspeed_video_get_dv_timings, ++ .vidioc_query_dv_timings = aspeed_video_query_dv_timings, ++ .vidioc_enum_dv_timings = aspeed_video_enum_dv_timings, ++ .vidioc_dv_timings_cap = aspeed_video_dv_timings_cap, ++ ++ .vidioc_subscribe_event = aspeed_video_sub_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++static void aspeed_video_update_jpeg_quality(struct aspeed_video *video) ++{ ++ u32 comp_ctrl = FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) | ++ FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10); ++ ++ aspeed_video_update(video, VE_COMP_CTRL, ++ VE_COMP_CTRL_DCT_LUM | VE_COMP_CTRL_DCT_CHR, ++ comp_ctrl); ++} ++ ++static void aspeed_video_update_subsampling(struct aspeed_video *video) ++{ ++ if (video->jpeg.virt) ++ aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420); ++ ++ if (video->yuv420) ++ aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_YUV420); ++ else ++ aspeed_video_update(video, VE_SEQ_CTRL, VE_SEQ_CTRL_YUV420, 0); ++} ++ ++static int aspeed_video_set_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct aspeed_video *video = container_of(ctrl->handler, ++ struct aspeed_video, ++ ctrl_handler); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_JPEG_COMPRESSION_QUALITY: ++ video->jpeg_quality = ctrl->val; ++ aspeed_video_update_jpeg_quality(video); ++ break; ++ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: ++ if (ctrl->val == V4L2_JPEG_CHROMA_SUBSAMPLING_420) { ++ video->yuv420 = true; ++ aspeed_video_update_subsampling(video); ++ } else { ++ video->yuv420 = false; ++ aspeed_video_update_subsampling(video); ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops aspeed_video_ctrl_ops = { ++ .s_ctrl = aspeed_video_set_ctrl, ++}; ++ ++static void aspeed_video_resolution_work(struct work_struct *work) ++{ ++ struct delayed_work *dwork = to_delayed_work(work); ++ struct aspeed_video *video = container_of(dwork, struct aspeed_video, ++ res_work); ++ u32 input_status = video->v4l2_input_status; ++ ++ aspeed_video_on(video); ++ ++ /* Exit early in case no clients remain */ ++ if (test_bit(VIDEO_STOPPED, &video->flags)) ++ goto done; ++ ++ aspeed_video_init_regs(video); ++ ++ aspeed_video_get_resolution(video); ++ ++ if (video->detected_timings.width != video->active_timings.width || ++ video->detected_timings.height != video->active_timings.height || ++ input_status != video->v4l2_input_status) { ++ static const struct v4l2_event ev = { ++ .type = V4L2_EVENT_SOURCE_CHANGE, ++ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, ++ }; ++ ++ v4l2_event_queue(&video->vdev, &ev); ++ } else if (test_bit(VIDEO_STREAMING, &video->flags)) { ++ /* No resolution change so just restart streaming */ ++ aspeed_video_start_frame(video); ++ } ++ ++done: ++ clear_bit(VIDEO_RES_CHANGE, &video->flags); ++ wake_up_interruptible_all(&video->wait); ++} ++ ++static int aspeed_video_open(struct file *file) ++{ ++ int rc; ++ struct aspeed_video *video = video_drvdata(file); ++ ++ mutex_lock(&video->video_lock); ++ ++ rc = v4l2_fh_open(file); ++ if (rc) { ++ mutex_unlock(&video->video_lock); ++ return rc; ++ } ++ ++ if (v4l2_fh_is_singular_file(file)) ++ aspeed_video_start(video); ++ ++ mutex_unlock(&video->video_lock); ++ ++ return 0; ++} ++ ++static int aspeed_video_release(struct file *file) ++{ ++ int rc; ++ struct aspeed_video *video = video_drvdata(file); ++ ++ mutex_lock(&video->video_lock); ++ ++ if (v4l2_fh_is_singular_file(file)) ++ aspeed_video_stop(video); ++ ++ rc = _vb2_fop_release(file, NULL); ++ ++ mutex_unlock(&video->video_lock); ++ ++ return rc; ++} ++ ++static const struct v4l2_file_operations aspeed_video_v4l2_fops = { ++ .owner = THIS_MODULE, ++ .read = vb2_fop_read, ++ .poll = vb2_fop_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = vb2_fop_mmap, ++ .open = aspeed_video_open, ++ .release = aspeed_video_release, ++}; ++ ++static int aspeed_video_queue_setup(struct vb2_queue *q, ++ unsigned int *num_buffers, ++ unsigned int *num_planes, ++ unsigned int sizes[], ++ struct device *alloc_devs[]) ++{ ++ struct aspeed_video *video = vb2_get_drv_priv(q); ++ ++ if (*num_planes) { ++ if (sizes[0] < video->max_compressed_size) ++ return -EINVAL; ++ ++ return 0; ++ } ++ ++ *num_planes = 1; ++ sizes[0] = video->max_compressed_size; ++ ++ return 0; ++} ++ ++static int aspeed_video_buf_prepare(struct vb2_buffer *vb) ++{ ++ struct aspeed_video *video = vb2_get_drv_priv(vb->vb2_queue); ++ ++ if (vb2_plane_size(vb, 0) < video->max_compressed_size) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int aspeed_video_start_streaming(struct vb2_queue *q, ++ unsigned int count) ++{ ++ int rc; ++ struct aspeed_video *video = vb2_get_drv_priv(q); ++ ++ video->sequence = 0; ++ ++ rc = aspeed_video_start_frame(video); ++ if (rc) { ++ aspeed_video_bufs_done(video, VB2_BUF_STATE_QUEUED); ++ return rc; ++ } ++ ++ set_bit(VIDEO_STREAMING, &video->flags); ++ return 0; ++} ++ ++static void aspeed_video_stop_streaming(struct vb2_queue *q) ++{ ++ int rc; ++ struct aspeed_video *video = vb2_get_drv_priv(q); ++ ++ clear_bit(VIDEO_STREAMING, &video->flags); ++ ++ rc = wait_event_timeout(video->wait, ++ !test_bit(VIDEO_FRAME_INPRG, &video->flags), ++ STOP_TIMEOUT); ++ if (!rc) { ++ dev_err(video->dev, "Timed out when stopping streaming\n"); ++ ++ /* ++ * Need to force stop any DMA and try and get HW into a good ++ * state for future calls to start streaming again. ++ */ ++ aspeed_video_reset(video); ++ aspeed_video_init_regs(video); ++ ++ aspeed_video_get_resolution(video); ++ } ++ ++ aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR); ++} ++ ++static void aspeed_video_buf_queue(struct vb2_buffer *vb) ++{ ++ bool empty; ++ struct aspeed_video *video = vb2_get_drv_priv(vb->vb2_queue); ++ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); ++ struct aspeed_video_buffer *avb = to_aspeed_video_buffer(vbuf); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&video->lock, flags); ++ empty = list_empty(&video->buffers); ++ list_add_tail(&avb->link, &video->buffers); ++ spin_unlock_irqrestore(&video->lock, flags); ++ ++ if (test_bit(VIDEO_STREAMING, &video->flags) && ++ !test_bit(VIDEO_FRAME_INPRG, &video->flags) && empty) ++ aspeed_video_start_frame(video); ++} ++ ++static const struct vb2_ops aspeed_video_vb2_ops = { ++ .queue_setup = aspeed_video_queue_setup, ++ .wait_prepare = vb2_ops_wait_prepare, ++ .wait_finish = vb2_ops_wait_finish, ++ .buf_prepare = aspeed_video_buf_prepare, ++ .start_streaming = aspeed_video_start_streaming, ++ .stop_streaming = aspeed_video_stop_streaming, ++ .buf_queue = aspeed_video_buf_queue, ++}; ++ ++static int aspeed_video_setup_video(struct aspeed_video *video) ++{ ++ const u64 mask = ~(BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_444) | ++ BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_420)); ++ struct v4l2_device *v4l2_dev = &video->v4l2_dev; ++ struct vb2_queue *vbq = &video->queue; ++ struct video_device *vdev = &video->vdev; ++ int rc; ++ ++ video->pix_fmt.pixelformat = V4L2_PIX_FMT_JPEG; ++ video->pix_fmt.field = V4L2_FIELD_NONE; ++ video->pix_fmt.colorspace = V4L2_COLORSPACE_SRGB; ++ video->pix_fmt.quantization = V4L2_QUANTIZATION_FULL_RANGE; ++ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; ++ ++ rc = v4l2_device_register(video->dev, v4l2_dev); ++ if (rc) { ++ dev_err(video->dev, "Failed to register v4l2 device\n"); ++ return rc; ++ } ++ ++ v4l2_ctrl_handler_init(&video->ctrl_handler, 2); ++ v4l2_ctrl_new_std(&video->ctrl_handler, &aspeed_video_ctrl_ops, ++ V4L2_CID_JPEG_COMPRESSION_QUALITY, 0, ++ ASPEED_VIDEO_JPEG_NUM_QUALITIES - 1, 1, 0); ++ v4l2_ctrl_new_std_menu(&video->ctrl_handler, &aspeed_video_ctrl_ops, ++ V4L2_CID_JPEG_CHROMA_SUBSAMPLING, ++ V4L2_JPEG_CHROMA_SUBSAMPLING_420, mask, ++ V4L2_JPEG_CHROMA_SUBSAMPLING_444); ++ ++ if (video->ctrl_handler.error) { ++ v4l2_ctrl_handler_free(&video->ctrl_handler); ++ v4l2_device_unregister(v4l2_dev); ++ ++ dev_err(video->dev, "Failed to init controls: %d\n", ++ video->ctrl_handler.error); ++ return rc; ++ } ++ ++ v4l2_dev->ctrl_handler = &video->ctrl_handler; ++ ++ vbq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ vbq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; ++ vbq->dev = v4l2_dev->dev; ++ vbq->lock = &video->video_lock; ++ vbq->ops = &aspeed_video_vb2_ops; ++ vbq->mem_ops = &vb2_dma_contig_memops; ++ vbq->drv_priv = video; ++ vbq->buf_struct_size = sizeof(struct aspeed_video_buffer); ++ vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ++ vbq->min_buffers_needed = 3; ++ ++ rc = vb2_queue_init(vbq); ++ if (rc) { ++ v4l2_ctrl_handler_free(&video->ctrl_handler); ++ v4l2_device_unregister(v4l2_dev); ++ ++ dev_err(video->dev, "Failed to init vb2 queue\n"); ++ return rc; ++ } ++ ++ vdev->queue = vbq; ++ vdev->fops = &aspeed_video_v4l2_fops; ++ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | ++ V4L2_CAP_STREAMING; ++ vdev->v4l2_dev = v4l2_dev; ++ strscpy(vdev->name, DEVICE_NAME, sizeof(vdev->name)); ++ vdev->vfl_type = VFL_TYPE_GRABBER; ++ vdev->vfl_dir = VFL_DIR_RX; ++ vdev->release = video_device_release_empty; ++ vdev->ioctl_ops = &aspeed_video_ioctl_ops; ++ vdev->lock = &video->video_lock; ++ ++ video_set_drvdata(vdev, video); ++ rc = video_register_device(vdev, VFL_TYPE_GRABBER, 0); ++ if (rc) { ++ vb2_queue_release(vbq); ++ v4l2_ctrl_handler_free(&video->ctrl_handler); ++ v4l2_device_unregister(v4l2_dev); ++ ++ dev_err(video->dev, "Failed to register video device\n"); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static int aspeed_video_init(struct aspeed_video *video) ++{ ++ int irq; ++ int rc; ++ struct device *dev = video->dev; ++ ++ irq = irq_of_parse_and_map(dev->of_node, 0); ++ if (!irq) { ++ dev_err(dev, "Unable to find IRQ\n"); ++ return -ENODEV; ++ } ++ ++ rc = devm_request_irq(dev, irq, aspeed_video_irq, IRQF_SHARED, ++ DEVICE_NAME, video); ++ if (rc < 0) { ++ dev_err(dev, "Unable to request IRQ %d\n", irq); ++ return rc; ++ } ++ ++ video->eclk = devm_clk_get(dev, "eclk"); ++ if (IS_ERR(video->eclk)) { ++ dev_err(dev, "Unable to get ECLK\n"); ++ return PTR_ERR(video->eclk); ++ } ++ ++ video->vclk = devm_clk_get(dev, "vclk"); ++ if (IS_ERR(video->vclk)) { ++ dev_err(dev, "Unable to get VCLK\n"); ++ return PTR_ERR(video->vclk); ++ } ++ ++ video->rst = devm_reset_control_get_exclusive(dev, NULL); ++ if (IS_ERR(video->rst)) { ++ dev_err(dev, "Unable to get VE reset\n"); ++ return PTR_ERR(video->rst); ++ } ++ ++ rc = of_reserved_mem_device_init(dev); ++ if (rc) { ++ dev_err(dev, "Unable to reserve memory\n"); ++ return rc; ++ } ++ ++ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); ++ if (rc) { ++ dev_err(dev, "Failed to set DMA mask\n"); ++ of_reserved_mem_device_release(dev); ++ return rc; ++ } ++ ++ if (!aspeed_video_alloc_buf(video, &video->jpeg, ++ VE_JPEG_HEADER_SIZE)) { ++ dev_err(dev, "Failed to allocate DMA for JPEG header\n"); ++ of_reserved_mem_device_release(dev); ++ return rc; ++ } ++ ++ aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420); ++ ++ return 0; ++} ++ ++static int aspeed_video_probe(struct platform_device *pdev) ++{ ++ int rc; ++ struct resource *res; ++ struct aspeed_video *video = kzalloc(sizeof(*video), GFP_KERNEL); ++ ++ if (!video) ++ return -ENOMEM; ++ ++ video->frame_rate = 30; ++ video->dev = &pdev->dev; ++ mutex_init(&video->video_lock); ++ init_waitqueue_head(&video->wait); ++ INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work); ++ INIT_LIST_HEAD(&video->buffers); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ video->base = devm_ioremap_resource(video->dev, res); ++ ++ if (IS_ERR(video->base)) ++ return PTR_ERR(video->base); ++ ++ rc = aspeed_video_init(video); ++ if (rc) ++ return rc; ++ ++ rc = aspeed_video_setup_video(video); ++ if (rc) ++ return rc; ++ ++ return 0; ++} ++ ++static int aspeed_video_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); ++ struct aspeed_video *video = to_aspeed_video(v4l2_dev); ++ ++ video_unregister_device(&video->vdev); ++ ++ vb2_queue_release(&video->queue); ++ ++ v4l2_ctrl_handler_free(&video->ctrl_handler); ++ ++ v4l2_device_unregister(v4l2_dev); ++ ++ dma_free_coherent(video->dev, VE_JPEG_HEADER_SIZE, video->jpeg.virt, ++ video->jpeg.dma); ++ ++ of_reserved_mem_device_release(dev); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_video_of_match[] = { ++ { .compatible = "aspeed,ast2400-video-engine" }, ++ { .compatible = "aspeed,ast2500-video-engine" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, aspeed_video_of_match); ++ ++static struct platform_driver aspeed_video_driver = { ++ .driver = { ++ .name = DEVICE_NAME, ++ .of_match_table = aspeed_video_of_match, ++ }, ++ .probe = aspeed_video_probe, ++ .remove = aspeed_video_remove, ++}; ++ ++module_platform_driver(aspeed_video_driver); ++ ++MODULE_DESCRIPTION("ASPEED Video Engine Driver"); ++MODULE_AUTHOR("Eddie James"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h +index f43738607d77..15a9059d0303 100644 +--- a/include/dt-bindings/clock/aspeed-clock.h ++++ b/include/dt-bindings/clock/aspeed-clock.h +@@ -50,5 +50,6 @@ + #define ASPEED_RESET_I2C 7 + #define ASPEED_RESET_AHB 8 + #define ASPEED_RESET_CRT1 9 ++#define ASPEED_RESET_VIDEO 10 + + #endif +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch new file mode 100644 index 000000000..7400e2848 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch @@ -0,0 +1,702 @@ +From a771e5448ed259f768434d498daf8d8b292713de Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Mon, 11 Feb 2019 17:02:35 -0800 +Subject: [PATCH] Add Aspeed PWM driver which uses FTTMR010 timer IP + +This commit adds Aspeed PWM driver which uses timer pulse output +feature in Aspeed SoCs. The timer IP is derived from Faraday +Technologies FTTMR010 IP but has some customized register +structure changes only for Aspeed SoCs. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + arch/arm/boot/dts/aspeed-g5.dtsi | 2 +- + drivers/clocksource/timer-fttmr010.c | 25 ++ + drivers/input/misc/pwm-beeper.c | 8 +- + drivers/pwm/Kconfig | 9 + + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-fttmr010.c | 465 +++++++++++++++++++++++++++++++++++ + include/clocksource/timer-fttmr010.h | 17 ++ + 7 files changed, 522 insertions(+), 5 deletions(-) + create mode 100644 drivers/pwm/pwm-fttmr010.c + create mode 100644 include/clocksource/timer-fttmr010.h + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 6686a13a5354..ccf2845cd788 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -301,7 +301,7 @@ + + timer: timer@1e782000 { + /* This timer is a Faraday FTTMR010 derivative */ +- compatible = "aspeed,ast2400-timer"; ++ compatible = "aspeed,ast2500-timer"; + reg = <0x1e782000 0x90>; + interrupts = <16 17 18 35 36 37 38 39>; + clocks = <&syscon ASPEED_CLK_APB>; +diff --git a/drivers/clocksource/timer-fttmr010.c b/drivers/clocksource/timer-fttmr010.c +index cf93f6419b51..8226ccf5cc2c 100644 +--- a/drivers/clocksource/timer-fttmr010.c ++++ b/drivers/clocksource/timer-fttmr010.c +@@ -20,6 +20,8 @@ + #include <linux/bitops.h> + #include <linux/delay.h> + ++#include <clocksource/timer-fttmr010.h> ++ + /* + * Register definitions for the timers + */ +@@ -77,6 +79,9 @@ + #define TIMER_3_INT_OVERFLOW BIT(8) + #define TIMER_INT_ALL_MASK 0x1ff + ++DEFINE_SPINLOCK(timer_fttmr010_lock); ++EXPORT_SYMBOL(timer_fttmr010_lock); ++ + struct fttmr010 { + void __iomem *base; + unsigned int tick_rate; +@@ -123,8 +128,11 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, + struct clock_event_device *evt) + { + struct fttmr010 *fttmr010 = to_fttmr010(evt); ++ unsigned long flags; + u32 cr; + ++ spin_lock_irqsave(&timer_fttmr010_lock, flags); ++ + /* Stop */ + cr = readl(fttmr010->base + TIMER_CR); + cr &= ~fttmr010->t1_enable_val; +@@ -147,27 +155,37 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, + cr |= fttmr010->t1_enable_val; + writel(cr, fttmr010->base + TIMER_CR); + ++ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); ++ + return 0; + } + + static int fttmr010_timer_shutdown(struct clock_event_device *evt) + { + struct fttmr010 *fttmr010 = to_fttmr010(evt); ++ unsigned long flags; + u32 cr; + ++ spin_lock_irqsave(&timer_fttmr010_lock, flags); ++ + /* Stop */ + cr = readl(fttmr010->base + TIMER_CR); + cr &= ~fttmr010->t1_enable_val; + writel(cr, fttmr010->base + TIMER_CR); + ++ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); ++ + return 0; + } + + static int fttmr010_timer_set_oneshot(struct clock_event_device *evt) + { + struct fttmr010 *fttmr010 = to_fttmr010(evt); ++ unsigned long flags; + u32 cr; + ++ spin_lock_irqsave(&timer_fttmr010_lock, flags); ++ + /* Stop */ + cr = readl(fttmr010->base + TIMER_CR); + cr &= ~fttmr010->t1_enable_val; +@@ -186,6 +204,8 @@ static int fttmr010_timer_set_oneshot(struct clock_event_device *evt) + cr |= TIMER_1_INT_MATCH1; + writel(cr, fttmr010->base + TIMER_INTR_MASK); + ++ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); ++ + return 0; + } + +@@ -193,8 +213,11 @@ static int fttmr010_timer_set_periodic(struct clock_event_device *evt) + { + struct fttmr010 *fttmr010 = to_fttmr010(evt); + u32 period = DIV_ROUND_CLOSEST(fttmr010->tick_rate, HZ); ++ unsigned long flags; + u32 cr; + ++ spin_lock_irqsave(&timer_fttmr010_lock, flags); ++ + /* Stop */ + cr = readl(fttmr010->base + TIMER_CR); + cr &= ~fttmr010->t1_enable_val; +@@ -221,6 +244,8 @@ static int fttmr010_timer_set_periodic(struct clock_event_device *evt) + cr |= fttmr010->t1_enable_val; + writel(cr, fttmr010->base + TIMER_CR); + ++ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); ++ + return 0; + } + +diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c +index edca0d737750..a3baa52f187f 100644 +--- a/drivers/input/misc/pwm-beeper.c ++++ b/drivers/input/misc/pwm-beeper.c +@@ -52,7 +52,7 @@ static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period) + if (error) + return error; + +- if (!beeper->amplifier_on) { ++ if (beeper->amplifier && !beeper->amplifier_on) { + error = regulator_enable(beeper->amplifier); + if (error) { + pwm_disable(beeper->pwm); +@@ -67,7 +67,7 @@ static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period) + + static void pwm_beeper_off(struct pwm_beeper *beeper) + { +- if (beeper->amplifier_on) { ++ if (beeper->amplifier && beeper->amplifier_on) { + regulator_disable(beeper->amplifier); + beeper->amplifier_on = false; + } +@@ -163,9 +163,9 @@ static int pwm_beeper_probe(struct platform_device *pdev) + if (IS_ERR(beeper->amplifier)) { + error = PTR_ERR(beeper->amplifier); + if (error != -EPROBE_DEFER) +- dev_err(dev, "Failed to get 'amp' regulator: %d\n", ++ dev_dbg(dev, "Failed to get 'amp' regulator: %d\n", + error); +- return error; ++ beeper->amplifier = NULL; + } + + INIT_WORK(&beeper->work, pwm_beeper_work); +diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig +index 504d252716f2..9d4642c668c9 100644 +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -168,6 +168,15 @@ config PWM_FSL_FTM + To compile this driver as a module, choose M here: the module + will be called pwm-fsl-ftm. + ++config PWM_FTTMR010 ++ tristate "Faraday Technology FTTMR010 timer PWM support" ++ help ++ Generic PWM framework driver for Faraday Technology FTTMR010 Timer ++ PWM output ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-fttmr010 ++ + config PWM_HIBVT + tristate "HiSilicon BVT PWM support" + depends on ARCH_HISI || COMPILE_TEST +diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile +index 9c676a0dadf5..13b7b20ad5ab 100644 +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -15,6 +15,7 @@ obj-$(CONFIG_PWM_CRC) += pwm-crc.o + obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o + obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o + obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o ++obj-$(CONFIG_PWM_FTTMR010) += pwm-fttmr010.o + obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o + obj-$(CONFIG_PWM_IMG) += pwm-img.o + obj-$(CONFIG_PWM_IMX) += pwm-imx.o +diff --git a/drivers/pwm/pwm-fttmr010.c b/drivers/pwm/pwm-fttmr010.c +new file mode 100644 +index 000000000000..459ace3eba6a +--- /dev/null ++++ b/drivers/pwm/pwm-fttmr010.c +@@ -0,0 +1,465 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2019 Intel Corporation ++ ++#include <linux/clk.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/pwm.h> ++ ++/* For timer_fttmr010_lock */ ++#include <clocksource/timer-fttmr010.h> ++ ++#define TIMER_CR 0x30 ++ ++#define TIMER5_ASPEED_COUNT 0x50 ++#define TIMER5_ASPEED_LOAD 0x54 ++#define TIMER5_ASPEED_MATCH1 0x58 ++#define TIMER5_ASPEED_MATCH2 0x5c ++#define TIMER6_ASPEED_COUNT 0x60 ++#define TIMER6_ASPEED_LOAD 0x64 ++#define TIMER6_ASPEED_MATCH1 0x68 ++#define TIMER6_ASPEED_MATCH2 0x6c ++#define TIMER7_ASPEED_COUNT 0x70 ++#define TIMER7_ASPEED_LOAD 0x74 ++#define TIMER7_ASPEED_MATCH1 0x78 ++#define TIMER7_ASPEED_MATCH2 0x7c ++#define TIMER8_ASPEED_COUNT 0x80 ++#define TIMER8_ASPEED_LOAD 0x84 ++#define TIMER8_ASPEED_MATCH1 0x88 ++#define TIMER8_ASPEED_MATCH2 0x8c ++ ++#define TIMER_5_CR_ASPEED_ENABLE BIT(16) ++#define TIMER_5_CR_ASPEED_CLOCK BIT(17) ++#define TIMER_5_CR_ASPEED_INT BIT(18) ++#define TIMER_5_CR_ASPEED_PULSE_OUT BIT(19) ++#define TIMER_6_CR_ASPEED_ENABLE BIT(20) ++#define TIMER_6_CR_ASPEED_CLOCK BIT(21) ++#define TIMER_6_CR_ASPEED_INT BIT(22) ++#define TIMER_6_CR_ASPEED_PULSE_OUT BIT(23) ++#define TIMER_7_CR_ASPEED_ENABLE BIT(24) ++#define TIMER_7_CR_ASPEED_CLOCK BIT(25) ++#define TIMER_7_CR_ASPEED_INT BIT(26) ++#define TIMER_7_CR_ASPEED_PULSE_OUT BIT(27) ++#define TIMER_8_CR_ASPEED_ENABLE BIT(28) ++#define TIMER_8_CR_ASPEED_CLOCK BIT(29) ++#define TIMER_8_CR_ASPEED_INT BIT(30) ++#define TIMER_8_CR_ASPEED_PULSE_OUT BIT(31) ++ ++/** ++ * struct pwm_fttmr010_variant - variant data depends on SoC ++ * @bits: timer counter resolution ++ * @chan_min: lowest timer channel which has pwm pulse output ++ * @chan_max: highest timer channel which has pwm pulse output ++ * @output_mask: pwm pulse output mask which is defined in device tree ++ */ ++struct pwm_fttmr010_variant { ++ u8 bits; ++ u8 chan_min; ++ u8 chan_max; ++ u8 output_mask; ++}; ++ ++/** ++ * struct pwm_fttmr010_chan - private data of FTTMR010 PWM channel ++ * @period_ns: current period in nanoseconds programmed to the hardware ++ * @duty_ns: current duty time in nanoseconds programmed to the hardware ++ */ ++struct pwm_fttmr010_chan { ++ u32 period_ns; ++ u32 duty_ns; ++}; ++ ++/** ++ * struct pwm_fttmr010 - private data of FTTMR010 PWM ++ * @chip: generic PWM chip ++ * @variant: local copy of hardware variant data ++ * @disabled_mask: disabled status for all channels - one bit per channel ++ * @base: base address of mapped PWM registers ++ * @clk: clock used to drive the timers ++ */ ++struct pwm_fttmr010 { ++ struct pwm_chip chip; ++ struct pwm_fttmr010_variant variant; ++ u8 disabled_mask; ++ void __iomem *base; ++ struct clk *clk; ++ u32 clk_tick_ns; ++}; ++ ++#if !defined(CONFIG_FTTMR010_TIMER) ++/* ++ * Timer block is shared between timer-fttmr010 and pwm-fttmr010 drivers ++ * and some registers need access synchronization. If both drivers are ++ * compiled in, the spinlock is defined in the clocksource driver, ++ * otherwise following definition is used. ++ * ++ * Currently we do not need any more complex synchronization method ++ * because all the supported SoCs contain only one instance of the Timer ++ * IP. Once this changes, both drivers will need to be modified to ++ * properly synchronize accesses to particular instances. ++ */ ++static DEFINE_SPINLOCK(timer_fttmr010_lock); ++#endif ++ ++static inline ++struct pwm_fttmr010 *to_pwm_fttmr010(struct pwm_chip *chip) ++{ ++ return container_of(chip, struct pwm_fttmr010, chip); ++} ++ ++static int pwm_fttmr010_request(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); ++ struct pwm_fttmr010_chan *chan; ++ ++ if (!(priv->variant.output_mask & BIT(pwm->hwpwm))) { ++ dev_warn(chip->dev, ++ "tried to request PWM channel %d without output\n", ++ pwm->hwpwm); ++ return -EINVAL; ++ } ++ ++ chan = devm_kzalloc(chip->dev, sizeof(*chan), GFP_KERNEL); ++ if (!chan) ++ return -ENOMEM; ++ ++ pwm_set_chip_data(pwm, chan); ++ ++ return 0; ++} ++ ++static void pwm_fttmr010_free(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ devm_kfree(chip->dev, pwm_get_chip_data(pwm)); ++ pwm_set_chip_data(pwm, NULL); ++} ++ ++static int pwm_fttmr010_enable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); ++ ulong flags; ++ u32 cr; ++ ++ spin_lock_irqsave(&timer_fttmr010_lock, flags); ++ ++ cr = readl(priv->base + TIMER_CR); ++ ++ switch (pwm->hwpwm) { ++ case 5: ++ cr |= (TIMER_5_CR_ASPEED_ENABLE | TIMER_5_CR_ASPEED_PULSE_OUT); ++ break; ++ case 6: ++ cr |= (TIMER_6_CR_ASPEED_ENABLE | TIMER_6_CR_ASPEED_PULSE_OUT); ++ break; ++ case 7: ++ cr |= (TIMER_7_CR_ASPEED_ENABLE | TIMER_7_CR_ASPEED_PULSE_OUT); ++ break; ++ case 8: ++ cr |= (TIMER_8_CR_ASPEED_ENABLE | TIMER_8_CR_ASPEED_PULSE_OUT); ++ break; ++ } ++ ++ writel(cr, priv->base + TIMER_CR); ++ ++ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); ++ ++ priv->disabled_mask &= ~BIT(pwm->hwpwm); ++ ++ return 0; ++} ++ ++static void pwm_fttmr010_disable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); ++ ulong flags; ++ u32 cr; ++ ++ spin_lock_irqsave(&timer_fttmr010_lock, flags); ++ ++ cr = readl(priv->base + TIMER_CR); ++ ++ switch (pwm->hwpwm) { ++ case 5: ++ cr &= ~(TIMER_5_CR_ASPEED_ENABLE | TIMER_5_CR_ASPEED_PULSE_OUT); ++ break; ++ case 6: ++ cr &= ~(TIMER_6_CR_ASPEED_ENABLE | TIMER_6_CR_ASPEED_PULSE_OUT); ++ break; ++ case 7: ++ cr &= ~(TIMER_7_CR_ASPEED_ENABLE | TIMER_7_CR_ASPEED_PULSE_OUT); ++ break; ++ case 8: ++ cr &= ~(TIMER_8_CR_ASPEED_ENABLE | TIMER_8_CR_ASPEED_PULSE_OUT); ++ break; ++ } ++ ++ writel(cr, priv->base + TIMER_CR); ++ ++ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); ++ ++ priv->disabled_mask |= BIT(pwm->hwpwm); ++} ++ ++static int pwm_fttmr010_config(struct pwm_chip *chip, struct pwm_device *pwm, ++ int duty_ns, int period_ns) ++{ ++ u32 tload, tmatch, creg_offset, lreg_offset, mreg_offset; ++ struct pwm_fttmr010_chan *chan = pwm_get_chip_data(pwm); ++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip); ++ ++ /* ++ * We currently avoid using 64bit arithmetic by using the ++ * fact that anything faster than 1Hz is easily representable ++ * by 32bits. ++ */ ++ if (period_ns > NSEC_PER_SEC) ++ return -ERANGE; ++ ++ /* No need to update */ ++ if (chan->period_ns == period_ns || chan->duty_ns == duty_ns) ++ return 0; ++ ++ tload = period_ns / priv->clk_tick_ns; ++ ++ /* Period is too short */ ++ if (tload <= 1) ++ return -ERANGE; ++ ++ tmatch = duty_ns / priv->clk_tick_ns; ++ ++ /* 0% duty is not available */ ++ if (!tmatch) ++ ++tmatch; ++ ++ tmatch = tload - tmatch; ++ ++ /* Decrement to get tick numbers, instead of tick counts */ ++ --tload; ++ --tmatch; ++ ++ if (tload == 0 || tmatch == 0) ++ return -ERANGE; ++ ++ dev_dbg(priv->chip.dev, "clk_tick_ns:%u, tload:%u, tmatch:%u\n", ++ priv->clk_tick_ns, tload, tmatch); ++ ++ switch (pwm->hwpwm) { ++ case 5: ++ creg_offset = TIMER5_ASPEED_COUNT; ++ lreg_offset = TIMER5_ASPEED_LOAD; ++ mreg_offset = TIMER5_ASPEED_MATCH1; ++ break; ++ case 6: ++ creg_offset = TIMER6_ASPEED_COUNT; ++ lreg_offset = TIMER6_ASPEED_LOAD; ++ mreg_offset = TIMER6_ASPEED_MATCH1; ++ break; ++ case 7: ++ creg_offset = TIMER7_ASPEED_COUNT; ++ lreg_offset = TIMER7_ASPEED_LOAD; ++ mreg_offset = TIMER7_ASPEED_MATCH1; ++ break; ++ case 8: ++ creg_offset = TIMER8_ASPEED_COUNT; ++ lreg_offset = TIMER8_ASPEED_LOAD; ++ mreg_offset = TIMER8_ASPEED_MATCH1; ++ break; ++ } ++ ++ writel(tload, priv->base + creg_offset); ++ writel(tload, priv->base + lreg_offset); ++ writel(tmatch, priv->base + mreg_offset); ++ ++ chan->period_ns = period_ns; ++ chan->duty_ns = duty_ns; ++ ++ return 0; ++} ++ ++static const struct pwm_ops pwm_fttmr010_ops = { ++ .request = pwm_fttmr010_request, ++ .free = pwm_fttmr010_free, ++ .enable = pwm_fttmr010_enable, ++ .disable = pwm_fttmr010_disable, ++ .config = pwm_fttmr010_config, ++ .owner = THIS_MODULE, ++}; ++ ++#ifdef CONFIG_OF ++static const struct pwm_fttmr010_variant aspeed_variant = { ++ .bits = 32, ++ .chan_min = 5, ++ .chan_max = 8, ++}; ++ ++static const struct of_device_id pwm_fttmr010_matches[] = { ++ { .compatible = "aspeed,ast2400-timer", .data = &aspeed_variant }, ++ { .compatible = "aspeed,ast2500-timer", .data = &aspeed_variant }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, pwm_fttmr010_matches); ++ ++static int pwm_fttmr010_parse_dt(struct pwm_fttmr010 *priv) ++{ ++ struct device_node *np = priv->chip.dev->of_node; ++ const struct of_device_id *match; ++ struct property *prop; ++ const __be32 *cur; ++ u32 val; ++ ++ match = of_match_node(pwm_fttmr010_matches, np); ++ if (!match) ++ return -ENODEV; ++ ++ memcpy(&priv->variant, match->data, sizeof(priv->variant)); ++ ++ of_property_for_each_u32(np, "fttmr010,pwm-outputs", prop, cur, val) { ++ if (val < priv->variant.chan_min || ++ val > priv->variant.chan_max) { ++ dev_err(priv->chip.dev, ++ "invalid channel index in fttmr010,pwm-outputs property\n"); ++ continue; ++ } ++ priv->variant.output_mask |= BIT(val); ++ } ++ ++ return 0; ++} ++#else ++static int pwm_fttmr010_parse_dt(struct pwm_fttmr010 *priv) ++{ ++ return -ENODEV; ++} ++#endif ++ ++static int pwm_fttmr010_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct pwm_fttmr010 *priv; ++ struct resource *res; ++ ulong clk_rate; ++ int ret; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->chip.dev = &pdev->dev; ++ priv->chip.ops = &pwm_fttmr010_ops; ++ priv->chip.base = -1; ++ ++ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { ++ ret = pwm_fttmr010_parse_dt(priv); ++ if (ret) ++ return ret; ++ ++ priv->chip.of_xlate = of_pwm_xlate_with_flags; ++ priv->chip.of_pwm_n_cells = 3; ++ } else { ++ if (!pdev->dev.platform_data) { ++ dev_err(&pdev->dev, "no platform data specified\n"); ++ return -EINVAL; ++ } ++ ++ memcpy(&priv->variant, pdev->dev.platform_data, ++ sizeof(priv->variant)); ++ } ++ ++ priv->chip.npwm = priv->variant.chan_max + 1; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ priv->clk = devm_clk_get(&pdev->dev, "PCLK"); ++ if (IS_ERR(priv->clk)) { ++ dev_err(dev, "failed to get timer base clk\n"); ++ return PTR_ERR(priv->clk); ++ } ++ ++ ret = clk_prepare_enable(priv->clk); ++ if (ret < 0) { ++ dev_err(dev, "failed to enable base clock\n"); ++ return ret; ++ } ++ ++ clk_rate = clk_get_rate(priv->clk); ++ priv->clk_tick_ns = NSEC_PER_SEC / clk_rate; ++ ++ platform_set_drvdata(pdev, priv); ++ ++ ret = pwmchip_add(&priv->chip); ++ if (ret < 0) { ++ dev_err(dev, "failed to register PWM chip\n"); ++ clk_disable_unprepare(priv->clk); ++ return ret; ++ } ++ ++ dev_dbg(dev, "clk at %lu\n", clk_rate); ++ ++ return 0; ++} ++ ++static int pwm_fttmr010_remove(struct platform_device *pdev) ++{ ++ struct pwm_fttmr010 *priv = platform_get_drvdata(pdev); ++ int ret; ++ ++ ret = pwmchip_remove(&priv->chip); ++ if (ret < 0) ++ return ret; ++ ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int pwm_fttmr010_resume(struct device *dev) ++{ ++ struct pwm_fttmr010 *priv = dev_get_drvdata(dev); ++ struct pwm_chip *chip = &priv->chip; ++ unsigned int i; ++ ++ for (i = chip->variant.chan_min; i < chip->variant.chan_max; i++) { ++ struct pwm_device *pwm = &chip->pwms[i]; ++ struct pwm_fttmr010_chan *chan = pwm_get_chip_data(pwm); ++ ++ if (!chan) ++ continue; ++ ++ if (chan->period_ns) { ++ pwm_fttmr010_config(chip, pwm, chan->duty_ns, ++ chan->period_ns); ++ } ++ ++ if (priv->disabled_mask & BIT(i)) ++ pwm_fttmr010_disable(chip, pwm); ++ else ++ pwm_fttmr010_enable(chip, pwm); ++ } ++ ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(pwm_fttmr010_pm_ops, NULL, pwm_fttmr010_resume); ++ ++static struct platform_driver pwm_fttmr010_driver = { ++ .driver = { ++ .name = "fttmr010-timer-pwm", ++ .pm = &pwm_fttmr010_pm_ops, ++ .of_match_table = of_match_ptr(pwm_fttmr010_matches), ++ }, ++ .probe = pwm_fttmr010_probe, ++ .remove = pwm_fttmr010_remove, ++}; ++module_platform_driver(pwm_fttmr010_driver); ++ ++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); ++MODULE_DESCRIPTION("FTTMR010 PWM Driver for timer pulse outputs"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/clocksource/timer-fttmr010.h b/include/clocksource/timer-fttmr010.h +new file mode 100644 +index 000000000000..d8d6a2f14130 +--- /dev/null ++++ b/include/clocksource/timer-fttmr010.h +@@ -0,0 +1,17 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#ifndef __CLOCKSOURCE_TIMER_FTTMR010_H ++#define __CLOCKSOURCE_TIMER_FTTMR010_H ++ ++#include <linux/spinlock.h> ++ ++/* ++ * Following declaration must be in an ifdef due to this symbol being static ++ * in timer-fttmr010 driver if the clocksource driver is not compiled in and the ++ * spinlock is not shared between both drivers. ++ */ ++#ifdef CONFIG_FTTMR010_TIMER ++extern spinlock_t timer_fttmr010_lock; ++#endif ++ ++#endif /* __CLOCKSOURCE_TIMER_FTTMR010_H */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch new file mode 100644 index 000000000..85b2f45a1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch @@ -0,0 +1,492 @@ +From 38ba0a960fcd17f7b3480fe3025c261fd60fe979 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Fri, 15 Feb 2019 16:05:09 -0800 +Subject: [PATCH] i2c: Add mux hold/unhold msg types + +This commit adds mux hold/unhold message types to support extended +mux control for IPMB and MCTP devices. A hold or an unhold message +can be added at the end of I2C message stream wrapped by +repeated-start, also can be used as a single message independantly. + +This mux hold/unhold message will be delivered throughout all mux +levels in the path. Means that if it goes to multi-level mux path, +all muxes will be held/unheld by this message. + +1. Hold message + struct i2c_msg msg; + uint16_t timeout = 5000; // timeout in ms. 5 secs in this example. + + msg.addr = 0x0; // any value can be used. addr will be ignored in this packet. + msg.flags = I2C_M_HOLD; // set this flag to indicate it's a hold message. + msg.len = sizeof(uint16_t); // timeout value will be delivered using two bytes buffer. + msg.buf = (uint8_t *)&timeout; // set timeout value. + +2. Unhold message + struct i2c_msg msg; + uint16_t timeout = 0; // set 0 for an unhold message. + + msg.addr = 0x0; // any value can be used. addr will be ignored in this packet. + msg.flags = I2C_M_HOLD; // set this flag to indicate it's an unhold message. + msg.len = sizeof(uint16_t); // timeout value will be delivered using two bytes buffer. + msg.buf = (uint8_t *)&timeout; // set timeout value. + + This unhold message can be delivered to a mux adapter even when + a bus is locked so that any holding state can be unheld + immediately by invoking this unhold message. + +This patch would not be welcomed from upstream so it should be kept +in downstream only. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + drivers/i2c/i2c-core-base.c | 79 +++++++++++++++++++++++++++---- + drivers/i2c/i2c-core-smbus.c | 17 ++++++- + drivers/i2c/i2c-mux.c | 109 +++++++++++++++++++++++++++++++++++++++---- + include/linux/i2c-mux.h | 3 ++ + include/linux/i2c.h | 25 ++++++++++ + include/uapi/linux/i2c.h | 1 + + 6 files changed, 215 insertions(+), 19 deletions(-) + +diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c +index 9200e349f29e..728b818501b1 100644 +--- a/drivers/i2c/i2c-core-base.c ++++ b/drivers/i2c/i2c-core-base.c +@@ -1211,6 +1211,25 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr) + } + EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify); + ++static void i2c_adapter_hold(struct i2c_adapter *adapter, unsigned long timeout) ++{ ++ mutex_lock(&adapter->hold_lock); ++ mod_timer(&adapter->hold_timer, jiffies + timeout); ++} ++ ++static void i2c_adapter_unhold(struct i2c_adapter *adapter) ++{ ++ del_timer_sync(&adapter->hold_timer); ++ mutex_unlock(&adapter->hold_lock); ++} ++ ++static void i2c_adapter_hold_timer_callback(struct timer_list *t) ++{ ++ struct i2c_adapter *adapter = from_timer(adapter, t, hold_timer); ++ ++ i2c_adapter_unhold(adapter); ++} ++ + static int i2c_register_adapter(struct i2c_adapter *adap) + { + int res = -EINVAL; +@@ -1292,6 +1311,9 @@ static int i2c_register_adapter(struct i2c_adapter *adap) + bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); + mutex_unlock(&core_lock); + ++ mutex_init(&adap->hold_lock); ++ timer_setup(&adap->hold_timer, i2c_adapter_hold_timer_callback, 0); ++ + return 0; + + out_reg: +@@ -1512,6 +1534,8 @@ void i2c_del_adapter(struct i2c_adapter *adap) + idr_remove(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); + ++ i2c_adapter_unhold(adap); ++ + /* Clear the device structure in case this adapter is ever going to be + added again */ + memset(&adap->dev, 0, sizeof(adap->dev)); +@@ -1861,7 +1885,9 @@ static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs, + */ + int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + { ++ enum i2c_hold_msg_type hold_msg; + unsigned long orig_jiffies; ++ unsigned long timeout; + int ret, try; + + if (WARN_ON(!msgs || num < 1)) +@@ -1870,6 +1896,25 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + if (adap->quirks && i2c_check_for_quirks(adap, msgs, num)) + return -EOPNOTSUPP; + ++ /* Do not deliver a mux hold msg to root bus adapter */ ++ if (!i2c_parent_is_i2c_adapter(adap)) { ++ hold_msg = i2c_check_hold_msg(msgs[num - 1].flags, ++ msgs[num - 1].len, ++ (u16 *)msgs[num - 1].buf); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(*(u16 *)msgs[num - 1].buf); ++ i2c_adapter_hold(adap, timeout); ++ ++ if (--num == 0) ++ return 0; ++ } else if (hold_msg == I2C_HOLD_MSG_RESET) { ++ i2c_adapter_unhold(adap); ++ return 0; ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&adap->hold_lock); ++ } ++ } ++ + /* + * i2c_trace_msg_key gets enabled when tracepoint i2c_transfer gets + * enabled. This is an efficient way of keeping the for-loop from +@@ -1902,6 +1947,9 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + trace_i2c_result(adap, num, ret); + } + ++ if (!i2c_parent_is_i2c_adapter(adap) && hold_msg == I2C_HOLD_MSG_NONE) ++ mutex_unlock(&adap->hold_lock); ++ + return ret; + } + EXPORT_SYMBOL(__i2c_transfer); +@@ -1920,6 +1968,7 @@ EXPORT_SYMBOL(__i2c_transfer); + */ + int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + { ++ bool do_bus_lock = true; + int ret; + + /* REVISIT the fault reporting model here is weak: +@@ -1949,18 +1998,30 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : ""); + } + #endif +- +- if (in_atomic() || irqs_disabled()) { +- ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT); +- if (!ret) +- /* I2C activity is ongoing. */ +- return -EAGAIN; +- } else { +- i2c_lock_bus(adap, I2C_LOCK_SEGMENT); ++ /* ++ * Do not lock a bus for delivering an unhold msg to a mux ++ * adpater. This is just for a single length unhold msg case. ++ */ ++ if (num == 1 && i2c_parent_is_i2c_adapter(adap) && ++ i2c_check_hold_msg(msgs[0].flags, msgs[0].len, ++ (u16 *)msgs[0].buf) == ++ I2C_HOLD_MSG_RESET) ++ do_bus_lock = false; ++ ++ if (do_bus_lock) { ++ if (in_atomic() || irqs_disabled()) { ++ ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT); ++ if (!ret) ++ /* I2C activity is ongoing. */ ++ return -EAGAIN; ++ } else { ++ i2c_lock_bus(adap, I2C_LOCK_SEGMENT); ++ } + } + + ret = __i2c_transfer(adap, msgs, num); +- i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); ++ if (do_bus_lock) ++ i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); + + return ret; + } else { +diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c +index 9cd66cabb84f..64c58911bf21 100644 +--- a/drivers/i2c/i2c-core-smbus.c ++++ b/drivers/i2c/i2c-core-smbus.c +@@ -528,12 +528,25 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, + u8 command, int protocol, union i2c_smbus_data *data) + { ++ bool do_bus_lock = true; + s32 res; + +- i2c_lock_bus(adapter, I2C_LOCK_SEGMENT); ++ /* ++ * Do not lock a bus for delivering an unhold msg to a mux adpater. ++ * This is just for a single length unhold msg case. ++ */ ++ if (i2c_parent_is_i2c_adapter(adapter) && ++ i2c_check_hold_msg(flags, ++ protocol == I2C_SMBUS_WORD_DATA ? 2 : 0, ++ &data->word) == I2C_HOLD_MSG_RESET) ++ do_bus_lock = false; ++ ++ if (do_bus_lock) ++ i2c_lock_bus(adapter, I2C_LOCK_SEGMENT); + res = __i2c_smbus_xfer(adapter, addr, flags, read_write, + command, protocol, data); +- i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT); ++ if (do_bus_lock) ++ i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT); + + return res; + } +diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c +index f330690b4125..4d8909a0f90a 100644 +--- a/drivers/i2c/i2c-mux.c ++++ b/drivers/i2c/i2c-mux.c +@@ -26,6 +26,7 @@ + #include <linux/module.h> + #include <linux/of.h> + #include <linux/slab.h> ++#include <linux/timer.h> + + /* multiplexer per channel data */ + struct i2c_mux_priv { +@@ -35,21 +36,57 @@ struct i2c_mux_priv { + u32 chan_id; + }; + ++static void i2c_mux_hold(struct i2c_mux_core *muxc, unsigned long timeout) ++{ ++ mutex_lock(&muxc->hold_lock); ++ mod_timer(&muxc->hold_timer, jiffies + timeout); ++} ++ ++static void i2c_mux_unhold(struct i2c_mux_core *muxc) ++{ ++ del_timer_sync(&muxc->hold_timer); ++ mutex_unlock(&muxc->hold_lock); ++} ++ ++static void i2c_mux_hold_timer_callback(struct timer_list *t) ++{ ++ struct i2c_mux_core *muxc = from_timer(muxc, t, hold_timer); ++ ++ i2c_mux_unhold(muxc); ++} ++ + static int __i2c_mux_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) + { + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; ++ enum i2c_hold_msg_type hold_msg; ++ unsigned long timeout; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + ++ hold_msg = i2c_check_hold_msg(msgs[num - 1].flags, ++ msgs[num - 1].len, ++ (u16 *)msgs[num - 1].buf); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(*(u16 *)msgs[num - 1].buf); ++ i2c_mux_hold(muxc, timeout); ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&muxc->hold_lock); ++ } + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = __i2c_transfer(parent, msgs, num); +- if (muxc->deselect) +- muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg != I2C_HOLD_MSG_SET) { ++ if (muxc->deselect) ++ muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg == I2C_HOLD_MSG_RESET) ++ i2c_mux_unhold(muxc); ++ else ++ mutex_unlock(&muxc->hold_lock); ++ } + + return ret; + } +@@ -60,15 +97,32 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap, + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; ++ enum i2c_hold_msg_type hold_msg; ++ unsigned long timeout; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + ++ hold_msg = i2c_check_hold_msg(msgs[num - 1].flags, ++ msgs[num - 1].len, ++ (u16 *)msgs[num - 1].buf); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(*(u16 *)msgs[num - 1].buf); ++ i2c_mux_hold(muxc, timeout); ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&muxc->hold_lock); ++ } + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = i2c_transfer(parent, msgs, num); +- if (muxc->deselect) +- muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg != I2C_HOLD_MSG_SET) { ++ if (muxc->deselect) ++ muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg == I2C_HOLD_MSG_RESET) ++ i2c_mux_unhold(muxc); ++ else ++ mutex_unlock(&muxc->hold_lock); ++ } + + return ret; + } +@@ -81,16 +135,33 @@ static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap, + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; ++ enum i2c_hold_msg_type hold_msg; ++ unsigned long timeout; + int ret; + + /* Select the right mux port and perform the transfer. */ + ++ hold_msg = i2c_check_hold_msg(flags, ++ size == I2C_SMBUS_WORD_DATA ? 2 : 0, ++ &data->word); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(data->word); ++ i2c_mux_hold(muxc, timeout); ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&muxc->hold_lock); ++ } + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = __i2c_smbus_xfer(parent, addr, flags, + read_write, command, size, data); +- if (muxc->deselect) +- muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg != I2C_HOLD_MSG_SET) { ++ if (muxc->deselect) ++ muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg == I2C_HOLD_MSG_RESET) ++ i2c_mux_unhold(muxc); ++ else ++ mutex_unlock(&muxc->hold_lock); ++ } + + return ret; + } +@@ -103,16 +174,33 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; ++ enum i2c_hold_msg_type hold_msg; ++ unsigned long timeout; + int ret; + + /* Select the right mux port and perform the transfer. */ + ++ hold_msg = i2c_check_hold_msg(flags, ++ size == I2C_SMBUS_WORD_DATA ? 2 : 0, ++ &data->word); ++ if (hold_msg == I2C_HOLD_MSG_SET) { ++ timeout = msecs_to_jiffies(data->word); ++ i2c_mux_hold(muxc, timeout); ++ } else if (hold_msg == I2C_HOLD_MSG_NONE) { ++ mutex_lock(&muxc->hold_lock); ++ } + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = i2c_smbus_xfer(parent, addr, flags, + read_write, command, size, data); +- if (muxc->deselect) +- muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg != I2C_HOLD_MSG_SET) { ++ if (muxc->deselect) ++ muxc->deselect(muxc, priv->chan_id); ++ if (hold_msg == I2C_HOLD_MSG_RESET) ++ i2c_mux_unhold(muxc); ++ else ++ mutex_unlock(&muxc->hold_lock); ++ } + + return ret; + } +@@ -263,6 +351,9 @@ struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, + muxc->deselect = deselect; + muxc->max_adapters = max_adapters; + ++ mutex_init(&muxc->hold_lock); ++ timer_setup(&muxc->hold_timer, i2c_mux_hold_timer_callback, 0); ++ + return muxc; + } + EXPORT_SYMBOL_GPL(i2c_mux_alloc); +@@ -435,6 +526,8 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc) + { + char symlink_name[20]; + ++ i2c_mux_unhold(muxc); ++ + while (muxc->num_adapters) { + struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters]; + struct i2c_mux_priv *priv = adap->algo_data; +diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h +index bd74d5706f3b..bc6f778eaf9d 100644 +--- a/include/linux/i2c-mux.h ++++ b/include/linux/i2c-mux.h +@@ -41,6 +41,9 @@ struct i2c_mux_core { + int (*select)(struct i2c_mux_core *, u32 chan_id); + int (*deselect)(struct i2c_mux_core *, u32 chan_id); + ++ struct mutex hold_lock; /* mutex for channel holding */ ++ struct timer_list hold_timer; ++ + int num_adapters; + int max_adapters; + struct i2c_adapter *adapter[0]; +diff --git a/include/linux/i2c.h b/include/linux/i2c.h +index 65b4eaed1d96..eadde70c0d4a 100644 +--- a/include/linux/i2c.h ++++ b/include/linux/i2c.h +@@ -692,6 +692,13 @@ struct i2c_adapter { + const struct i2c_adapter_quirks *quirks; + + struct irq_domain *host_notify_domain; ++ ++ /* ++ * These will be used by root adpaters only. For muxes, each mux core ++ * has these individually. ++ */ ++ struct mutex hold_lock; /* mutex for bus holding */ ++ struct timer_list hold_timer; + }; + #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) + +@@ -949,4 +956,22 @@ static inline struct i2c_client *i2c_acpi_new_device(struct device *dev, + } + #endif /* CONFIG_ACPI */ + ++enum i2c_hold_msg_type { ++ I2C_HOLD_MSG_NONE, ++ I2C_HOLD_MSG_SET, ++ I2C_HOLD_MSG_RESET ++}; ++ ++static inline enum i2c_hold_msg_type i2c_check_hold_msg(u16 flags, u16 len, u16 *buf) ++{ ++ if (flags & I2C_M_HOLD && len == sizeof(u16)) { ++ if (*buf) ++ return I2C_HOLD_MSG_SET; ++ ++ return I2C_HOLD_MSG_RESET; ++ } ++ ++ return I2C_HOLD_MSG_NONE; ++} ++ + #endif /* _LINUX_I2C_H */ +diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h +index f71a1751cacf..a1db9b17ed36 100644 +--- a/include/uapi/linux/i2c.h ++++ b/include/uapi/linux/i2c.h +@@ -72,6 +72,7 @@ struct i2c_msg { + #define I2C_M_RD 0x0001 /* read data, from slave to master */ + /* I2C_M_RD is guaranteed to be 0x0001! */ + #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ ++#define I2C_M_HOLD 0x0100 /* for holding a mux path */ + #define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */ + /* makes only sense in kernelspace */ + /* userspace buffers are copied anyway */ +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg new file mode 100644 index 000000000..41530dd6e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg @@ -0,0 +1 @@ +CONFIG_BLK_DEV_RAM=y diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend new file mode 100644 index 000000000..cdf4325d9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend @@ -0,0 +1,31 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +do_compile_prepend(){ + # device tree compiler flags + export DTC_FLAGS=-@ +} + +SRC_URI += " \ + file://intel.cfg \ + file://0005-arm-dts-aspeed-g5-add-espi.patch \ + file://0007-New-flash-map-for-intel.patch \ + file://0008-Add-ASPEED-SGPIO-driver.patch \ + file://0009-SGPIO-DT-and-pinctrl-fixup.patch \ + file://0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch \ + file://0019-Add-I2C-IPMB-support.patch \ + file://0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch \ + file://0022-Add-AST2500-eSPI-driver.patch \ + file://0025-dts-add-AST2500-LPC-SIO-tree-node.patch \ + file://0026-Add-support-for-new-PECI-commands.patch \ + file://0028-Add-AST2500-JTAG-driver.patch \ + file://0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch \ + file://0030-Add-dump-debug-code-into-I2C-drivers.patch \ + file://0031-Add-high-speed-baud-rate-support-for-UART.patch \ + file://0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch \ + file://0034-arm-dts-adpeed-Swap-the-mac-nodes-numbering.patch \ + file://0035-Implement-a-memory-driver-share-memory.patch \ + file://0036-net-ncsi-backport-ncsi-patches.patch \ + file://0038-media-aspeed-backport-ikvm-patches.patch \ + file://0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch \ + file://0040-i2c-Add-mux-hold-unhold-msg-types.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers/0001-Enable-passthrough-based-gpio-character-device.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers/0001-Enable-passthrough-based-gpio-character-device.patch new file mode 100644 index 000000000..15e845ebc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers/0001-Enable-passthrough-based-gpio-character-device.patch @@ -0,0 +1,26 @@ +From 61a5796d151337fd537d1aeb3fb072dcdf96abbe Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Sun, 3 Feb 2019 16:08:11 +0800 +Subject: [PATCH] Enable passthrough based gpio character device. + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + include/uapi/linux/gpio.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h +index 1bf6e6df..8883ca90 100644 +--- a/include/uapi/linux/gpio.h ++++ b/include/uapi/linux/gpio.h +@@ -62,7 +62,7 @@ struct gpioline_info { + #define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2) + #define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3) + #define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4) +- ++#define GPIOHANDLE_REQUEST_PASS_THROUGH (1UL << 5) + /** + * struct gpiohandle_request - Information about a GPIO handle request + * @lineoffsets: an array desired lines, specified by offset index for the +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers_%.bbappend b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers_%.bbappend new file mode 100644 index 000000000..703af71df --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers_%.bbappend @@ -0,0 +1,5 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += " \ + file://0001-Enable-passthrough-based-gpio-character-device.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/network/0001-Patch-to-keep-consistent-MAC-and-IP-address-inbetwee.patch b/meta-openbmc-mods/meta-common/recipes-network/network/network/0001-Patch-to-keep-consistent-MAC-and-IP-address-inbetwee.patch new file mode 100644 index 000000000..03460302d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/network/0001-Patch-to-keep-consistent-MAC-and-IP-address-inbetwee.patch @@ -0,0 +1,456 @@ +From 15f9ba436815307c1df7ace505e6f6ee04a4762b Mon Sep 17 00:00:00 2001 +From: David Cobbley <david.j.cobbley@linux.intel.com> +Date: Thu, 8 Mar 2018 12:18:00 -0800 +Subject: [PATCH 1/3] Patch to keep consistent MAC and IP address inbetween + power cycles + +Currently, your mac will reset upon AC cycle unless you ask systemd use +a MAC provided in your network configuration file. This will write your +randomly generate MAC to the config file upond first boot up. + +Change-Id: Id47d24c62e459cde101add18be2f46c0b010e7fe +Signed-off-by: David Cobbley <david.j.cobbley@linux.intel.com> +Signed-off-by: James Feist <james.feist@linux.intel.com> +--- + ethernet_interface.cpp | 4 +- + ethernet_interface.hpp | 360 +++++++++++++++++++++-------------------- + network_config.cpp | 22 +-- + 3 files changed, 195 insertions(+), 191 deletions(-) + +diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp +index 3fd7835..fd09b7a 100644 +--- a/ethernet_interface.cpp ++++ b/ethernet_interface.cpp +@@ -200,8 +200,8 @@ InterfaceInfo EthernetInterface::getInterfaceInfo() const + * @return macaddress on success + */ + +-std::string +- EthernetInterface::getMACAddress(const std::string& interfaceName) const ++std::string EthernetInterface::getMACAddress( ++ const std::string& interfaceName) + { + ifreq ifr{}; + char macAddress[mac_address::size]{}; +diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp +index bfe1d54..d62ca34 100644 +--- a/ethernet_interface.hpp ++++ b/ethernet_interface.hpp +@@ -59,185 +59,187 @@ using VlanInterfaceMap = + */ + class EthernetInterface : public Ifaces + { +- public: +- EthernetInterface() = delete; +- EthernetInterface(const EthernetInterface&) = delete; +- EthernetInterface& operator=(const EthernetInterface&) = delete; +- EthernetInterface(EthernetInterface&&) = delete; +- EthernetInterface& operator=(EthernetInterface&&) = delete; +- virtual ~EthernetInterface() = default; +- +- /** @brief Constructor to put object onto bus at a dbus path. +- * @param[in] bus - Bus to attach to. +- * @param[in] objPath - Path to attach at. +- * @param[in] dhcpEnabled - is dhcp enabled(true/false). +- * @param[in] parent - parent object. +- * @param[in] emitSignal - true if the object added signal needs to be +- * send. +- */ +- EthernetInterface(sdbusplus::bus::bus& bus, const std::string& objPath, +- bool dhcpEnabled, Manager& parent, +- bool emitSignal = true); +- +- /** @brief Function to create ipaddress dbus object. +- * @param[in] addressType - Type of ip address. +- * @param[in] ipaddress- IP address. +- * @param[in] prefixLength - Length of prefix. +- * @param[in] gateway - Gateway ip address. +- */ +- +- void iP(IP::Protocol addressType, std::string ipaddress, +- uint8_t prefixLength, std::string gateway) override; +- +- /* @brief delete the dbus object of the given ipaddress. +- * @param[in] ipaddress - IP address. +- */ +- void deleteObject(const std::string& ipaddress); +- +- /* @brief delete the vlan dbus object of the given interface. +- * Also deletes the device file and the network file. +- * @param[in] interface - VLAN Interface. +- */ +- void deleteVLANObject(const std::string& interface); +- +- /* @brief creates the dbus object(IPaddres) given in the address list. +- * @param[in] addrs - address list for which dbus objects needs +- * to create. +- */ +- void createIPAddressObjects(); +- +- /* @brief Gets all the ip addresses. +- * @returns the list of ipaddress. +- */ +- const AddressMap& getAddresses() const +- { +- return addrs; +- } +- +- /** Set value of DHCPEnabled */ +- bool dHCPEnabled(bool value) override; +- +- /** @brief sets the MAC address. +- * @param[in] value - MAC address which needs to be set on the system. +- * @returns macAddress of the interface or throws an error. +- */ +- std::string mACAddress(std::string value) override; +- +- /** @brief sets the NTP servers. +- * @param[in] value - vector of NTP servers. +- */ +- ServerList nTPServers(ServerList value) override; +- +- /** @brief sets the DNS/nameservers. +- * @param[in] value - vector of DNS servers. +- */ +- ServerList nameservers(ServerList value) override; +- +- /** @brief create Vlan interface. +- * @param[in] id- VLAN identifier. +- */ +- void createVLAN(VlanId id); +- +- /** @brief load the vlan info from the system +- * and creates the ip address dbus objects. +- * @param[in] vlanID- VLAN identifier. +- */ +- void loadVLAN(VlanId vlanID); +- +- /** @brief write the network conf file with the in-memory objects. +- */ +- void writeConfigurationFile(); +- +- /** @brief delete all dbus objects. +- */ +- void deleteAll(); +- +- using EthernetInterfaceIntf::dHCPEnabled; +- using EthernetInterfaceIntf::interfaceName; +- using MacAddressIntf::mACAddress; +- +- /** @brief Absolute path of the resolv conf file */ +- static constexpr auto resolvConfFile = "/etc/resolv.conf"; +- +- protected: +- /** @brief get the info of the ethernet interface. +- * @return tuple having the link speed,autonegotiation,duplexmode . +- */ +- InterfaceInfo getInterfaceInfo() const; +- +- /* @brief delete the vlan interface from system. +- * @param[in] interface - vlan Interface. +- */ +- void deleteVLANFromSystem(const std::string& interface); +- +- /** @brief get the mac address of the interface. +- * @param[in] interfaceName - Network interface name. +- * @return macaddress on success +- */ +- +- std::string getMACAddress(const std::string& interfaceName) const; +- +- /** @brief construct the ip address dbus object path. +- * @param[in] addressType - Type of ip address. +- * @param[in] ipaddress - IP address. +- * @param[in] prefixLength - Length of prefix. +- * @param[in] gateway - Gateway address. +- +- * @return path of the address object. +- */ +- +- std::string generateObjectPath(IP::Protocol addressType, +- const std::string& ipaddress, +- uint8_t prefixLength, +- const std::string& gateway) const; +- +- /** @brief generates the id by doing hash of ipaddress, +- * prefixlength and the gateway. +- * @param[in] ipaddress - IP address. +- * @param[in] prefixLength - Length of prefix. +- * @param[in] gateway - Gateway address. +- * @return hash string. +- */ +- +- static std::string generateId(const std::string& ipaddress, +- uint8_t prefixLength, +- const std::string& gateway); +- +- /** @brief write the dhcp section **/ +- void writeDHCPSection(std::fstream& stream); +- +- /** @brief get the NTP server list from the network conf +- * +- */ +- ServerList getNTPServersFromConf(); +- +- /** @brief write the DNS entries to resolver file. +- * @param[in] dnsList - DNS server list which needs to be written. +- * @param[in] file - File to write the name server entries to. +- */ +- void writeDNSEntries(const ServerList& dnsList, const std::string& file); +- +- /** @brief get the name server details from the network conf +- * +- */ +- ServerList getNameServerFromConf(); +- +- /** @brief Persistent sdbusplus DBus bus connection. */ +- sdbusplus::bus::bus& bus; +- +- /** @brief Network Manager object. */ +- Manager& manager; +- +- /** @brief Persistent map of IPAddress dbus objects and their names */ +- AddressMap addrs; +- +- /** @brief Persistent map of VLAN interface dbus objects and their names */ +- VlanInterfaceMap vlanInterfaces; +- +- /** @brief Dbus object path */ +- std::string objPath; +- +- friend class TestEthernetInterface; ++ public: ++ EthernetInterface() = delete; ++ EthernetInterface(const EthernetInterface&) = delete; ++ EthernetInterface& operator=(const EthernetInterface&) = delete; ++ EthernetInterface(EthernetInterface&&) = delete; ++ EthernetInterface& operator=(EthernetInterface&&) = delete; ++ virtual ~EthernetInterface() = default; ++ ++ /** @brief Constructor to put object onto bus at a dbus path. ++ * @param[in] bus - Bus to attach to. ++ * @param[in] objPath - Path to attach at. ++ * @param[in] dhcpEnabled - is dhcp enabled(true/false). ++ * @param[in] parent - parent object. ++ * @param[in] emitSignal - true if the object added signal needs to be ++ * send. ++ */ ++ EthernetInterface(sdbusplus::bus::bus& bus, ++ const std::string& objPath, ++ bool dhcpEnabled, ++ Manager& parent, ++ bool emitSignal = true); ++ ++ /** @brief Function to create ipaddress dbus object. ++ * @param[in] addressType - Type of ip address. ++ * @param[in] ipaddress- IP address. ++ * @param[in] prefixLength - Length of prefix. ++ * @param[in] gateway - Gateway ip address. ++ */ ++ ++ void iP(IP::Protocol addressType, ++ std::string ipaddress, ++ uint8_t prefixLength, ++ std::string gateway) override; ++ ++ /* @brief delete the dbus object of the given ipaddress. ++ * @param[in] ipaddress - IP address. ++ */ ++ void deleteObject(const std::string& ipaddress); ++ ++ /* @brief delete the vlan dbus object of the given interface. ++ * Also deletes the device file and the network file. ++ * @param[in] interface - VLAN Interface. ++ */ ++ void deleteVLANObject(const std::string& interface); ++ ++ /* @brief creates the dbus object(IPaddres) given in the address list. ++ * @param[in] addrs - address list for which dbus objects needs ++ * to create. ++ */ ++ void createIPAddressObjects(); ++ ++ /* @brief Gets all the ip addresses. ++ * @returns the list of ipaddress. ++ */ ++ const AddressMap& getAddresses() const { return addrs; } ++ ++ /** Set value of DHCPEnabled */ ++ bool dHCPEnabled(bool value) override; ++ ++ /** @brief sets the MAC address. ++ * @param[in] value - MAC address which needs to be set on the system. ++ * @returns macAddress of the interface or throws an error. ++ */ ++ std::string mACAddress(std::string value) override; ++ ++ /** @brief sets the NTP servers. ++ * @param[in] value - vector of NTP servers. ++ */ ++ ServerList nTPServers(ServerList value) override; ++ ++ /** @brief sets the DNS/nameservers. ++ * @param[in] value - vector of DNS servers. ++ */ ++ ServerList nameservers(ServerList value) override; ++ ++ /** @brief create Vlan interface. ++ * @param[in] id- VLAN identifier. ++ */ ++ void createVLAN(VlanId id); ++ ++ /** @brief load the vlan info from the system ++ * and creates the ip address dbus objects. ++ * @param[in] vlanID- VLAN identifier. ++ */ ++ void loadVLAN(VlanId vlanID); ++ ++ /** @brief write the network conf file with the in-memory objects. ++ */ ++ void writeConfigurationFile(); ++ ++ /** @brief delete all dbus objects. ++ */ ++ void deleteAll(); ++ ++ /** @brief get the mac address of the interface. ++ * @param[in] interfaceName - Network interface name. ++ * @return macaddress on success ++ */ ++ ++ static std::string getMACAddress(const std::string& interfaceName); ++ ++ using EthernetInterfaceIntf::dHCPEnabled; ++ using EthernetInterfaceIntf::interfaceName; ++ using MacAddressIntf::mACAddress; ++ ++ /** @brief Absolute path of the resolv conf file */ ++ static constexpr auto resolvConfFile = "/etc/resolv.conf"; ++ ++ protected: ++ /** @brief get the info of the ethernet interface. ++ * @return tuple having the link speed,autonegotiation,duplexmode . ++ */ ++ InterfaceInfo getInterfaceInfo() const; ++ ++ /* @brief delete the vlan interface from system. ++ * @param[in] interface - vlan Interface. ++ */ ++ void deleteVLANFromSystem(const std::string& interface); ++ ++ /** @brief construct the ip address dbus object path. ++ * @param[in] addressType - Type of ip address. ++ * @param[in] ipaddress - IP address. ++ * @param[in] prefixLength - Length of prefix. ++ * @param[in] gateway - Gateway address. ++ ++ * @return path of the address object. ++ */ ++ ++ std::string generateObjectPath(IP::Protocol addressType, ++ const std::string& ipaddress, ++ uint8_t prefixLength, ++ const std::string& gateway) const; ++ ++ /** @brief generates the id by doing hash of ipaddress, ++ * prefixlength and the gateway. ++ * @param[in] ipaddress - IP address. ++ * @param[in] prefixLength - Length of prefix. ++ * @param[in] gateway - Gateway address. ++ * @return hash string. ++ */ ++ ++ static std::string generateId(const std::string& ipaddress, ++ uint8_t prefixLength, ++ const std::string& gateway); ++ ++ /** @brief write the dhcp section **/ ++ void writeDHCPSection(std::fstream& stream);; ++ ++ /** @brief get the NTP server list from the network conf ++ * ++ */ ++ ServerList getNTPServersFromConf(); ++ ++ /** @brief write the DNS entries to resolver file. ++ * @param[in] dnsList - DNS server list which needs to be written. ++ * @param[in] file - File to write the name server entries to. ++ */ ++ void writeDNSEntries(const ServerList& dnsList, ++ const std::string& file); ++ ++ /** @brief get the name server details from the network conf ++ * ++ */ ++ ServerList getNameServerFromConf(); ++ ++ /** @brief Persistent sdbusplus DBus bus connection. */ ++ sdbusplus::bus::bus& bus; ++ ++ /** @brief Network Manager object. */ ++ Manager& manager; ++ ++ /** @brief Persistent map of IPAddress dbus objects and their names */ ++ AddressMap addrs; ++ ++ /** @brief Persistent map of VLAN interface dbus objects and their names */ ++ VlanInterfaceMap vlanInterfaces; ++ ++ /** @brief Dbus object path */ ++ std::string objPath; ++ ++ friend class TestEthernetInterface; + }; + + } // namespace network +diff --git a/network_config.cpp b/network_config.cpp +index e83b16c..8ebad54 100644 +--- a/network_config.cpp ++++ b/network_config.cpp +@@ -1,3 +1,5 @@ ++#include "network_config.hpp" ++#include "ethernet_interface.hpp" + #include "config.h" + + #include "network_config.hpp" +@@ -5,27 +7,27 @@ + #include <fstream> + #include <string> + +-namespace phosphor +-{ +-namespace network +-{ ++namespace phosphor { ++namespace network { + +-namespace bmc +-{ +-void writeDHCPDefault(const std::string& filename, const std::string& interface) ++namespace bmc { ++void writeDHCPDefault(const std::string &filename, const std::string &interface) + { ++ + std::ofstream filestream; + + filestream.open(filename); + filestream << "[Match]\nName=" << interface << +- "\n[Network]\nDHCP=true\n" ++ "\n[Network]\nDHCP=true\n" + #ifdef LINK_LOCAL_AUTOCONFIGURATION + "LinkLocalAddressing=yes\n" + #else + "LinkLocalAddressing=no\n" + #endif +- "IPv6AcceptRA=false\n" +- "[DHCP]\nClientIdentifier=mac\n"; ++ "IPv6AcceptRA=false\n" ++ "[DHCP]\nClientIdentifier=mac\n" ++ "[Link]\nMACAddress=" ++ << EthernetInterface::getMACAddress(interface) << "\n"; + filestream.close(); + } + } // namespace bmc +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/network/0002-IPv6-Network-changes-to-configuration-file.patch b/meta-openbmc-mods/meta-common/recipes-network/network/network/0002-IPv6-Network-changes-to-configuration-file.patch new file mode 100644 index 000000000..251f68319 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/network/0002-IPv6-Network-changes-to-configuration-file.patch @@ -0,0 +1,210 @@ +From ebb359773b8a5c03a25c3a48c5080bb246c07c71 Mon Sep 17 00:00:00 2001 +From: David Cobbley <david.j.cobbley@linux.intel.com> +Date: Wed, 6 Jun 2018 11:11:43 -0700 +Subject: [PATCH 2/3] IPv6 Network changes to configuration file + +Allow Additional parameters to be set for IPv6 + +Change-Id: If662f1ce2d265bc525073890c49231bf6f2b8a30 +--- + ethernet_interface.cpp | 109 +++++++++++++++++++++++++++++++++++++++-- + ethernet_interface.hpp | 19 ++++++- + util.cpp | 3 +- + 3 files changed, 124 insertions(+), 7 deletions(-) + +diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp +index fd09b7a..63f1160 100644 +--- a/ethernet_interface.cpp ++++ b/ethernet_interface.cpp +@@ -46,6 +46,8 @@ EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, + std::replace(intfName.begin(), intfName.end(), '_', '.'); + interfaceName(intfName); + EthernetInterfaceIntf::dHCPEnabled(dhcpEnabled); ++ EthernetInterfaceIntf::iPAddressEnables(getIPAddressEnablesFromConf()); ++ EthernetInterfaceIntf::iPv6AcceptRA(getIPv6AcceptRAFromConf()); + MacAddressIntf::mACAddress(getMACAddress(intfName)); + EthernetInterfaceIntf::nTPServers(getNTPServersFromConf()); + EthernetInterfaceIntf::nameservers(getNameServerFromConf()); +@@ -322,7 +324,16 @@ std::string EthernetInterface::generateObjectPath( + objectPath /= generateId(ipaddress, prefixLength, gateway); + return objectPath.string(); + } +- ++bool EthernetInterface::iPv6AcceptRA(bool value) ++{ ++ if (value == EthernetInterfaceIntf::iPv6AcceptRA()) ++ { ++ return value; ++ } ++ EthernetInterfaceIntf::iPv6AcceptRA(value); ++ manager.writeToConfigurationFile(); ++ return value; ++} + bool EthernetInterface::dHCPEnabled(bool value) + { + if (value == EthernetInterfaceIntf::dHCPEnabled()) +@@ -433,7 +444,80 @@ void EthernetInterface::createVLAN(VlanId id) + // write the new vlan device entry to the configuration(network) file. + manager.writeToConfigurationFile(); + } ++bool EthernetInterface::getIPv6AcceptRAFromConf() ++{ ++ fs::path confPath = manager.getConfDir(); ++ ++ std::string fileName = systemd::config::networkFilePrefix + ++ interfaceName() + systemd::config::networkFileSuffix; ++ confPath /= fileName; ++ config::ValueList values; ++ config::Parser parser(confPath.string()); ++ auto rc = config::ReturnCode::SUCCESS; ++ std::tie(rc, values) = parser.getValues("Network", "IPv6AcceptRA"); ++ if (rc != config::ReturnCode::SUCCESS) ++ { ++ log<level::DEBUG>("Unable to get the value for Network[IPv6AcceptRA]", ++ entry("rc=%d", rc)); ++ return false; ++ } ++ if (values[0] == "true") ++ { ++ return true; ++ } ++ ++ return false; ++} ++EthernetInterface::IPAllowed EthernetInterface::getIPAddressEnablesFromConf() ++{ ++ fs::path confPath = manager.getConfDir(); ++ ++ std::string fileName = systemd::config::networkFilePrefix + ++ interfaceName() + systemd::config::networkFileSuffix; ++ confPath /= fileName; ++ config::ValueList values; ++ config::Parser parser(confPath.string()); ++ auto rc = config::ReturnCode::SUCCESS; ++ std::tie(rc, values) = parser.getValues("Network", "DHCP"); ++ if (rc != config::ReturnCode::SUCCESS) ++ { ++ log<level::DEBUG>("Unable to get the value for Network[DHCP]", ++ entry("rc=%d", rc)); ++ return EthernetInterface::IPAllowed::IPv4AndIPv6; ++ } ++ // true, false, ipv4, ipv6 ++ if (values[0] == "ipv6") ++ { ++ return EthernetInterface::IPAllowed::IPv6Only; ++ } ++ else if (values[0] == "ipv4") ++ { ++ return EthernetInterface::IPAllowed::IPv4Only; ++ } ++ else if (values[0] == "off") ++ { ++ // This function should not get called if DHCP == off ++ log<level::DEBUG>("Function not available in static mode"); ++ return EthernetInterface::IPAllowed::IPv4AndIPv6; ++ } ++ else ++ { ++ return EthernetInterface::IPAllowed::IPv4AndIPv6; ++ } ++} ++EthernetInterface::IPAllowed ++ EthernetInterface::iPAddressEnables(EthernetInterface::IPAllowed iPAllowed) ++{ ++ if (iPAllowed == EthernetInterfaceIntf::iPAddressEnables()) ++ { ++ return iPAllowed; ++ } ++ ++ EthernetInterfaceIntf::iPAddressEnables(iPAllowed); ++ writeConfigurationFile(); + ++ return iPAllowed; ++} + ServerList EthernetInterface::getNTPServersFromConf() + { + fs::path confPath = manager.getConfDir(); +@@ -515,7 +599,8 @@ void EthernetInterface::writeConfigurationFile() + #else + stream << "LinkLocalAddressing=no\n"; + #endif +- stream << "IPv6AcceptRA=false\n"; ++ stream << std::boolalpha ++ << "IPv6AcceptRA=" << EthernetInterfaceIntf::iPv6AcceptRA() << "\n"; + + // Add the VLAN entry + for (const auto& intf : vlanInterfaces) +@@ -524,8 +609,24 @@ void EthernetInterface::writeConfigurationFile() + << "\n"; + } + // Add the DHCP entry +- auto value = dHCPEnabled() ? "true"s : "false"s; +- stream << "DHCP="s + value + "\n"; ++ std::string dhcpValue = "false"; ++ if (dHCPEnabled()) ++ { ++ IPAllowed ipAllowed = EthernetInterfaceIntf::iPAddressEnables(); ++ if (ipAllowed == IPAllowed::IPv4AndIPv6) ++ { ++ dhcpValue = "true"; ++ } ++ else if (ipAllowed == IPAllowed::IPv4Only) ++ { ++ dhcpValue = "ipv4"; ++ } ++ else if (ipAllowed == IPAllowed::IPv6Only) ++ { ++ dhcpValue = "ipv6"; ++ } ++ } ++ stream << "DHCP=" << dhcpValue << "\n"; + + // When the interface configured as dhcp, we don't need below given entries + // in config file. +diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp +index d62ca34..7116b47 100644 +--- a/ethernet_interface.hpp ++++ b/ethernet_interface.hpp +@@ -205,7 +205,24 @@ class EthernetInterface : public Ifaces + const std::string& gateway); + + /** @brief write the dhcp section **/ +- void writeDHCPSection(std::fstream& stream);; ++ void writeDHCPSection(std::fstream& stream); ++ ++ /** @brief get the IPv6AcceptRA flag from the network configuration file ++ * ++ */ ++ bool getIPv6AcceptRAFromConf(); ++ ++ /** @brief check conf file for Router Advertisements ++ * ++ */ ++ bool iPv6AcceptRA(bool value) override; ++ ++ /** @brief get the allowed network modes. Similar to DHCP enabled, but ++ * more specific ++ */ ++ IPAllowed getIPAddressEnablesFromConf(); ++ ++ IPAllowed iPAddressEnables(IPAllowed) override; + + /** @brief get the NTP server list from the network conf + * +diff --git a/util.cpp b/util.cpp +index b66f908..9f06e2e 100644 +--- a/util.cpp ++++ b/util.cpp +@@ -405,8 +405,7 @@ bool getDHCPValue(const std::string& confDir, const std::string& intf) + entry("RC=%d", rc)); + return dhcp; + } +- // There will be only single value for DHCP key. +- if (values[0] == "true") ++ if (values[0] != "false") + { + dhcp = true; + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/network/0003-Adding-channel-specific-privilege-to-network.patch b/meta-openbmc-mods/meta-common/recipes-network/network/network/0003-Adding-channel-specific-privilege-to-network.patch new file mode 100755 index 000000000..4610b8b32 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/network/0003-Adding-channel-specific-privilege-to-network.patch @@ -0,0 +1,800 @@ +From 64fff77b31de705a42c5061e9d14946255c6aca1 Mon Sep 17 00:00:00 2001 +From: AppaRao Puli <apparao.puli@linux.intel.com> +Date: Wed, 5 Sep 2018 14:16:54 +0530 +Subject: [PATCH] Adding channel specific privilege to network + + - Adding the channel access information to the network + interface object. This privilege will be used in + channel specific authorization. + - Get supported priv from user manager service dynamically. + - Signal handling for capturing the supported priv list + changes from user managerment. + +Tested-by: +Verified channel access through ipmitool get/set channel +access command + +Change-Id: I3b592a19363eef684e31d5f7c34dad8f2f9211df +Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + ethernet_interface.cpp | 119 +++++++++++++- + ethernet_interface.hpp | 433 ++++++++++++++++++++++++++----------------------- + network_manager.cpp | 104 ++++++++++++ + network_manager.hpp | 9 + + 4 files changed, 464 insertions(+), 201 deletions(-) + +diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp +index 9437b4c..6d23b3d 100644 +--- a/ethernet_interface.cpp ++++ b/ethernet_interface.cpp +@@ -35,6 +35,9 @@ using namespace phosphor::logging; + using namespace sdbusplus::xyz::openbmc_project::Common::Error; + using Argument = xyz::openbmc_project::Common::InvalidArgument; + ++static constexpr const char* networkChannelCfgFile = ++ "/var/channel_intf_data.json"; ++static constexpr const char* defaultChannelPriv = "priv-admin"; + EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, + const std::string& objPath, + bool dhcpEnabled, Manager& parent, +@@ -51,6 +54,7 @@ EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, + MacAddressIntf::mACAddress(getMACAddress(intfName)); + EthernetInterfaceIntf::nTPServers(getNTPServersFromConf()); + EthernetInterfaceIntf::nameservers(getNameServerFromConf()); ++ getChannelPrivilege(intfName); + + // Emit deferred signal. + if (emitSignal) +@@ -208,8 +212,7 @@ InterfaceInfo EthernetInterface::getInterfaceInfo() const + * @return macaddress on success + */ + +-std::string EthernetInterface::getMACAddress( +- const std::string& interfaceName) ++std::string EthernetInterface::getMACAddress(const std::string& interfaceName) + { + ifreq ifr{}; + char macAddress[mac_address::size]{}; +@@ -829,5 +832,117 @@ void EthernetInterface::deleteAll() + manager.writeToConfigurationFile(); + } + ++nlohmann::json EthernetInterface::readJsonFile(const std::string& configFile) ++{ ++ std::ifstream jsonFile(configFile); ++ if (!jsonFile.good()) ++ { ++ log<level::ERR>("JSON file not found"); ++ return nullptr; ++ } ++ ++ nlohmann::json data = nullptr; ++ try ++ { ++ data = nlohmann::json::parse(jsonFile, nullptr, false); ++ } ++ catch (nlohmann::json::parse_error& e) ++ { ++ log<level::DEBUG>("Corrupted channel config.", ++ entry("MSG: %s", e.what())); ++ throw std::runtime_error("Corrupted channel config file"); ++ } ++ ++ return data; ++} ++ ++int EthernetInterface::writeJsonFile(const std::string& configFile, ++ const nlohmann::json& jsonData) ++{ ++ std::ofstream jsonFile(configFile); ++ if (!jsonFile.good()) ++ { ++ log<level::ERR>("JSON file open failed", ++ entry("FILE=%s", networkChannelCfgFile)); ++ return -1; ++ } ++ ++ // Write JSON to file ++ jsonFile << jsonData; ++ ++ jsonFile.flush(); ++ return 0; ++} ++ ++std::string ++ EthernetInterface::getChannelPrivilege(const std::string& interfaceName) ++{ ++ std::string priv(defaultChannelPriv); ++ std::string retPriv; ++ ++ nlohmann::json jsonData = readJsonFile(networkChannelCfgFile); ++ if (jsonData != nullptr) ++ { ++ try ++ { ++ priv = jsonData[interfaceName].get<std::string>(); ++ retPriv = ChannelAccessIntf::maxPrivilege(std::move(priv)); ++ return retPriv; ++ } ++ catch (const nlohmann::json::exception& e) ++ { ++ jsonData[interfaceName] = priv; ++ } ++ } ++ else ++ { ++ jsonData[interfaceName] = priv; ++ } ++ ++ if (writeJsonFile(networkChannelCfgFile, jsonData) != 0) ++ { ++ log<level::DEBUG>("Error in write JSON data to file", ++ entry("FILE=%s", networkChannelCfgFile)); ++ elog<InternalFailure>(); ++ } ++ ++ retPriv = ChannelAccessIntf::maxPrivilege(std::move(priv)); ++ ++ return retPriv; ++} ++ ++std::string EthernetInterface::maxPrivilege(std::string priv) ++{ ++ std::string intfName = interfaceName(); ++ ++ if (!priv.empty() && (std::find(manager.supportedPrivList.begin(), ++ manager.supportedPrivList.end(), ++ priv) == manager.supportedPrivList.end())) ++ { ++ log<level::ERR>("Invalid privilege"); ++ elog<InvalidArgument>(Argument::ARGUMENT_NAME("Privilege"), ++ Argument::ARGUMENT_VALUE(priv.c_str())); ++ } ++ ++ if (ChannelAccessIntf::maxPrivilege() == priv) ++ { ++ // No change in privilege so just return. ++ return priv; ++ } ++ ++ nlohmann::json jsonData = readJsonFile(networkChannelCfgFile); ++ jsonData[intfName] = priv; ++ ++ if (writeJsonFile(networkChannelCfgFile, jsonData) != 0) ++ { ++ log<level::DEBUG>("Error in write JSON data to file", ++ entry("FILE=%s", networkChannelCfgFile)); ++ elog<InternalFailure>(); ++ } ++ ++ // Property change signal will be sent ++ return ChannelAccessIntf::maxPrivilege(std::move(priv)); ++} ++ + } // namespace network + } // namespace phosphor +diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp +index 7116b47..7b1da9a 100644 +--- a/ethernet_interface.hpp ++++ b/ethernet_interface.hpp +@@ -2,10 +2,13 @@ + + #include "types.hpp" + #include "util.hpp" ++#include "xyz/openbmc_project/Channel/ChannelAccess/server.hpp" + #include "xyz/openbmc_project/Network/IP/Create/server.hpp" + + #include <experimental/filesystem> ++#include <nlohmann/json.hpp> + #include <sdbusplus/bus.hpp> ++#include <sdbusplus/bus/match.hpp> + #include <sdbusplus/server/object.hpp> + #include <string> + #include <xyz/openbmc_project/Collection/DeleteAll/server.hpp> +@@ -21,7 +24,8 @@ using Ifaces = sdbusplus::server::object::object< + sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface, + sdbusplus::xyz::openbmc_project::Network::server::MACAddress, + sdbusplus::xyz::openbmc_project::Network::IP::server::Create, +- sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll>; ++ sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll, ++ sdbusplus::xyz::openbmc_project::Channel::server::ChannelAccess>; + + using IP = sdbusplus::xyz::openbmc_project::Network::server::IP; + +@@ -29,9 +33,14 @@ using EthernetInterfaceIntf = + sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface; + using MacAddressIntf = + sdbusplus::xyz::openbmc_project::Network::server::MACAddress; ++using ChannelAccessIntf = ++ sdbusplus::xyz::openbmc_project::Channel::server::ChannelAccess; + + using ServerList = std::vector<std::string>; + ++using DbusVariant = ++ sdbusplus::message::variant<std::string, std::vector<std::string>>; ++ + namespace fs = std::experimental::filesystem; + + class Manager; // forward declaration of network manager. +@@ -59,204 +68,230 @@ using VlanInterfaceMap = + */ + class EthernetInterface : public Ifaces + { +- public: +- EthernetInterface() = delete; +- EthernetInterface(const EthernetInterface&) = delete; +- EthernetInterface& operator=(const EthernetInterface&) = delete; +- EthernetInterface(EthernetInterface&&) = delete; +- EthernetInterface& operator=(EthernetInterface&&) = delete; +- virtual ~EthernetInterface() = default; +- +- /** @brief Constructor to put object onto bus at a dbus path. +- * @param[in] bus - Bus to attach to. +- * @param[in] objPath - Path to attach at. +- * @param[in] dhcpEnabled - is dhcp enabled(true/false). +- * @param[in] parent - parent object. +- * @param[in] emitSignal - true if the object added signal needs to be +- * send. +- */ +- EthernetInterface(sdbusplus::bus::bus& bus, +- const std::string& objPath, +- bool dhcpEnabled, +- Manager& parent, +- bool emitSignal = true); +- +- /** @brief Function to create ipaddress dbus object. +- * @param[in] addressType - Type of ip address. +- * @param[in] ipaddress- IP address. +- * @param[in] prefixLength - Length of prefix. +- * @param[in] gateway - Gateway ip address. +- */ +- +- void iP(IP::Protocol addressType, +- std::string ipaddress, +- uint8_t prefixLength, +- std::string gateway) override; +- +- /* @brief delete the dbus object of the given ipaddress. +- * @param[in] ipaddress - IP address. +- */ +- void deleteObject(const std::string& ipaddress); +- +- /* @brief delete the vlan dbus object of the given interface. +- * Also deletes the device file and the network file. +- * @param[in] interface - VLAN Interface. +- */ +- void deleteVLANObject(const std::string& interface); +- +- /* @brief creates the dbus object(IPaddres) given in the address list. +- * @param[in] addrs - address list for which dbus objects needs +- * to create. +- */ +- void createIPAddressObjects(); +- +- /* @brief Gets all the ip addresses. +- * @returns the list of ipaddress. +- */ +- const AddressMap& getAddresses() const { return addrs; } +- +- /** Set value of DHCPEnabled */ +- bool dHCPEnabled(bool value) override; +- +- /** @brief sets the MAC address. +- * @param[in] value - MAC address which needs to be set on the system. +- * @returns macAddress of the interface or throws an error. +- */ +- std::string mACAddress(std::string value) override; +- +- /** @brief sets the NTP servers. +- * @param[in] value - vector of NTP servers. +- */ +- ServerList nTPServers(ServerList value) override; +- +- /** @brief sets the DNS/nameservers. +- * @param[in] value - vector of DNS servers. +- */ +- ServerList nameservers(ServerList value) override; +- +- /** @brief create Vlan interface. +- * @param[in] id- VLAN identifier. +- */ +- void createVLAN(VlanId id); +- +- /** @brief load the vlan info from the system +- * and creates the ip address dbus objects. +- * @param[in] vlanID- VLAN identifier. +- */ +- void loadVLAN(VlanId vlanID); +- +- /** @brief write the network conf file with the in-memory objects. +- */ +- void writeConfigurationFile(); +- +- /** @brief delete all dbus objects. +- */ +- void deleteAll(); +- +- /** @brief get the mac address of the interface. +- * @param[in] interfaceName - Network interface name. +- * @return macaddress on success +- */ +- +- static std::string getMACAddress(const std::string& interfaceName); +- +- using EthernetInterfaceIntf::dHCPEnabled; +- using EthernetInterfaceIntf::interfaceName; +- using MacAddressIntf::mACAddress; +- +- /** @brief Absolute path of the resolv conf file */ +- static constexpr auto resolvConfFile = "/etc/resolv.conf"; +- +- protected: +- /** @brief get the info of the ethernet interface. +- * @return tuple having the link speed,autonegotiation,duplexmode . +- */ +- InterfaceInfo getInterfaceInfo() const; +- +- /* @brief delete the vlan interface from system. +- * @param[in] interface - vlan Interface. +- */ +- void deleteVLANFromSystem(const std::string& interface); +- +- /** @brief construct the ip address dbus object path. +- * @param[in] addressType - Type of ip address. +- * @param[in] ipaddress - IP address. +- * @param[in] prefixLength - Length of prefix. +- * @param[in] gateway - Gateway address. +- +- * @return path of the address object. +- */ +- +- std::string generateObjectPath(IP::Protocol addressType, +- const std::string& ipaddress, +- uint8_t prefixLength, +- const std::string& gateway) const; +- +- /** @brief generates the id by doing hash of ipaddress, +- * prefixlength and the gateway. +- * @param[in] ipaddress - IP address. +- * @param[in] prefixLength - Length of prefix. +- * @param[in] gateway - Gateway address. +- * @return hash string. +- */ +- +- static std::string generateId(const std::string& ipaddress, +- uint8_t prefixLength, +- const std::string& gateway); +- +- /** @brief write the dhcp section **/ +- void writeDHCPSection(std::fstream& stream); +- +- /** @brief get the IPv6AcceptRA flag from the network configuration file +- * +- */ +- bool getIPv6AcceptRAFromConf(); +- +- /** @brief check conf file for Router Advertisements +- * +- */ +- bool iPv6AcceptRA(bool value) override; +- +- /** @brief get the allowed network modes. Similar to DHCP enabled, but +- * more specific +- */ +- IPAllowed getIPAddressEnablesFromConf(); +- +- IPAllowed iPAddressEnables(IPAllowed) override; +- +- /** @brief get the NTP server list from the network conf +- * +- */ +- ServerList getNTPServersFromConf(); +- +- /** @brief write the DNS entries to resolver file. +- * @param[in] dnsList - DNS server list which needs to be written. +- * @param[in] file - File to write the name server entries to. +- */ +- void writeDNSEntries(const ServerList& dnsList, +- const std::string& file); +- +- /** @brief get the name server details from the network conf +- * +- */ +- ServerList getNameServerFromConf(); +- +- /** @brief Persistent sdbusplus DBus bus connection. */ +- sdbusplus::bus::bus& bus; +- +- /** @brief Network Manager object. */ +- Manager& manager; +- +- /** @brief Persistent map of IPAddress dbus objects and their names */ +- AddressMap addrs; +- +- /** @brief Persistent map of VLAN interface dbus objects and their names */ +- VlanInterfaceMap vlanInterfaces; +- +- /** @brief Dbus object path */ +- std::string objPath; +- +- friend class TestEthernetInterface; ++ public: ++ EthernetInterface() = delete; ++ EthernetInterface(const EthernetInterface&) = delete; ++ EthernetInterface& operator=(const EthernetInterface&) = delete; ++ EthernetInterface(EthernetInterface&&) = delete; ++ EthernetInterface& operator=(EthernetInterface&&) = delete; ++ virtual ~EthernetInterface() = default; ++ ++ /** @brief Constructor to put object onto bus at a dbus path. ++ * @param[in] bus - Bus to attach to. ++ * @param[in] objPath - Path to attach at. ++ * @param[in] dhcpEnabled - is dhcp enabled(true/false). ++ * @param[in] parent - parent object. ++ * @param[in] emitSignal - true if the object added signal needs to be ++ * send. ++ */ ++ EthernetInterface(sdbusplus::bus::bus& bus, const std::string& objPath, ++ bool dhcpEnabled, Manager& parent, ++ bool emitSignal = true); ++ ++ /** @brief Function to create ipaddress dbus object. ++ * @param[in] addressType - Type of ip address. ++ * @param[in] ipaddress- IP address. ++ * @param[in] prefixLength - Length of prefix. ++ * @param[in] gateway - Gateway ip address. ++ */ ++ ++ void iP(IP::Protocol addressType, std::string ipaddress, ++ uint8_t prefixLength, std::string gateway) override; ++ ++ /* @brief delete the dbus object of the given ipaddress. ++ * @param[in] ipaddress - IP address. ++ */ ++ void deleteObject(const std::string& ipaddress); ++ ++ /* @brief delete the vlan dbus object of the given interface. ++ * Also deletes the device file and the network file. ++ * @param[in] interface - VLAN Interface. ++ */ ++ void deleteVLANObject(const std::string& interface); ++ ++ /* @brief creates the dbus object(IPaddres) given in the address list. ++ * @param[in] addrs - address list for which dbus objects needs ++ * to create. ++ */ ++ void createIPAddressObjects(); ++ ++ /* @brief Gets all the ip addresses. ++ * @returns the list of ipaddress. ++ */ ++ const AddressMap& getAddresses() const ++ { ++ return addrs; ++ } ++ ++ /** Set value of DHCPEnabled */ ++ bool dHCPEnabled(bool value) override; ++ ++ /** @brief sets the MAC address. ++ * @param[in] value - MAC address which needs to be set on the system. ++ * @returns macAddress of the interface or throws an error. ++ */ ++ std::string mACAddress(std::string value) override; ++ ++ /** @brief sets the NTP servers. ++ * @param[in] value - vector of NTP servers. ++ */ ++ ServerList nTPServers(ServerList value) override; ++ ++ /** @brief sets the DNS/nameservers. ++ * @param[in] value - vector of DNS servers. ++ */ ++ ServerList nameservers(ServerList value) override; ++ ++ /** @brief create Vlan interface. ++ * @param[in] id- VLAN identifier. ++ */ ++ void createVLAN(VlanId id); ++ ++ /** @brief load the vlan info from the system ++ * and creates the ip address dbus objects. ++ * @param[in] vlanID- VLAN identifier. ++ */ ++ void loadVLAN(VlanId vlanID); ++ ++ /** @brief write the network conf file with the in-memory objects. ++ */ ++ void writeConfigurationFile(); ++ ++ /** @brief delete all dbus objects. ++ */ ++ void deleteAll(); ++ ++ /** @brief get the mac address of the interface. ++ * @param[in] interfaceName - Network interface name. ++ * @return macaddress on success ++ */ ++ ++ static std::string getMACAddress(const std::string& interfaceName); ++ ++ /** @brief sets the channel maxium privilege. ++ * @param[in] value - Channel privilege which needs to be set on the ++ * system. ++ * @returns privilege of the interface or throws an error. ++ */ ++ std::string maxPrivilege(std::string value) override; ++ ++ using ChannelAccessIntf::maxPrivilege; ++ using EthernetInterfaceIntf::dHCPEnabled; ++ using EthernetInterfaceIntf::interfaceName; ++ using MacAddressIntf::mACAddress; ++ ++ /** @brief Absolute path of the resolv conf file */ ++ static constexpr auto resolvConfFile = "/etc/resolv.conf"; ++ ++ protected: ++ /** @brief get the info of the ethernet interface. ++ * @return tuple having the link speed,autonegotiation,duplexmode . ++ */ ++ InterfaceInfo getInterfaceInfo() const; ++ ++ /* @brief delete the vlan interface from system. ++ * @param[in] interface - vlan Interface. ++ */ ++ void deleteVLANFromSystem(const std::string& interface); ++ ++ /** @brief construct the ip address dbus object path. ++ * @param[in] addressType - Type of ip address. ++ * @param[in] ipaddress - IP address. ++ * @param[in] prefixLength - Length of prefix. ++ * @param[in] gateway - Gateway address. ++ ++ * @return path of the address object. ++ */ ++ ++ std::string generateObjectPath(IP::Protocol addressType, ++ const std::string& ipaddress, ++ uint8_t prefixLength, ++ const std::string& gateway) const; ++ ++ /** @brief generates the id by doing hash of ipaddress, ++ * prefixlength and the gateway. ++ * @param[in] ipaddress - IP address. ++ * @param[in] prefixLength - Length of prefix. ++ * @param[in] gateway - Gateway address. ++ * @return hash string. ++ */ ++ ++ static std::string generateId(const std::string& ipaddress, ++ uint8_t prefixLength, ++ const std::string& gateway); ++ ++ /** @brief write the dhcp section **/ ++ void writeDHCPSection(std::fstream& stream); ++ ++ /** @brief get the IPv6AcceptRA flag from the network configuration file ++ * ++ */ ++ bool getIPv6AcceptRAFromConf(); ++ ++ /** @brief check conf file for Router Advertisements ++ * ++ */ ++ bool iPv6AcceptRA(bool value) override; ++ ++ /** @brief get the allowed network modes. Similar to DHCP enabled, but ++ * more specific ++ */ ++ IPAllowed getIPAddressEnablesFromConf(); ++ ++ IPAllowed iPAddressEnables(IPAllowed) override; ++ ++ /** @brief get the NTP server list from the network conf ++ * ++ */ ++ ServerList getNTPServersFromConf(); ++ ++ /** @brief write the DNS entries to resolver file. ++ * @param[in] dnsList - DNS server list which needs to be written. ++ * @param[in] file - File to write the name server entries to. ++ */ ++ void writeDNSEntries(const ServerList& dnsList, const std::string& file); ++ ++ /** @brief get the name server details from the network conf ++ * ++ */ ++ ServerList getNameServerFromConf(); ++ ++ /** @brief Persistent sdbusplus DBus bus connection. */ ++ sdbusplus::bus::bus& bus; ++ ++ /** @brief Network Manager object. */ ++ Manager& manager; ++ ++ /** @brief Persistent map of IPAddress dbus objects and their names */ ++ AddressMap addrs; ++ ++ /** @brief Persistent map of VLAN interface dbus objects and their names */ ++ VlanInterfaceMap vlanInterfaces; ++ ++ /** @brief Dbus object path */ ++ std::string objPath; ++ ++ friend class TestEthernetInterface; ++ ++ /** @brief gets the channel privilege. ++ * @param[in] interfaceName - Network interface name. ++ * @returns privilege of the interface ++ */ ++ std::string getChannelPrivilege(const std::string& interfaceName); ++ ++ /** @brief reads the channel access info from file. ++ * @param[in] configFile - channel access filename ++ * @returns json file data ++ */ ++ nlohmann::json readJsonFile(const std::string& configFile); ++ ++ /** @brief writes the channel access info to file. ++ * @param[in] configFile - channel access filename ++ * @param[in] jsonData - json data to write ++ * @returns success or failure ++ */ ++ int writeJsonFile(const std::string& configFile, ++ const nlohmann::json& jsonData); + }; + + } // namespace network +diff --git a/network_manager.cpp b/network_manager.cpp +index c4ab0da..c573d01 100644 +--- a/network_manager.cpp ++++ b/network_manager.cpp +@@ -30,6 +30,13 @@ extern std::unique_ptr<Timer> restartTimer; + using namespace phosphor::logging; + using namespace sdbusplus::xyz::openbmc_project::Common::Error; + ++static constexpr const char* userMgrObjBasePath = "/xyz/openbmc_project/user"; ++static constexpr const char* userMgrInterface = ++ "xyz.openbmc_project.User.Manager"; ++static constexpr const char* propNameAllPrivileges = "AllPrivileges"; ++ ++std::unique_ptr<sdbusplus::bus::match_t> usrMgmtSignal(nullptr); ++ + Manager::Manager(sdbusplus::bus::bus& bus, const char* objPath, + const std::string& path) : + details::VLANCreateIface(bus, objPath, true), +@@ -37,6 +44,103 @@ Manager::Manager(sdbusplus::bus::bus& bus, const char* objPath, + { + fs::path confDir(path); + setConfDir(confDir); ++ initSupportedPrivilges(); ++} ++ ++std::string getUserService(sdbusplus::bus::bus& bus, const std::string& intf, ++ const std::string& path) ++{ ++ auto mapperCall = ++ bus.new_method_call("xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetObject"); ++ ++ mapperCall.append(path); ++ mapperCall.append(std::vector<std::string>({intf})); ++ ++ auto mapperResponseMsg = bus.call(mapperCall); ++ ++ std::map<std::string, std::vector<std::string>> mapperResponse; ++ mapperResponseMsg.read(mapperResponse); ++ ++ if (mapperResponse.begin() == mapperResponse.end()) ++ { ++ throw std::runtime_error("ERROR in reading the mapper response"); ++ } ++ ++ return mapperResponse.begin()->first; ++} ++ ++std::string Manager::getUserServiceName() ++{ ++ static std::string userMgmtService; ++ if (userMgmtService.empty()) ++ { ++ try ++ { ++ userMgmtService = ++ getUserService(bus, userMgrInterface, userMgrObjBasePath); ++ } ++ catch (const std::exception& e) ++ { ++ log<level::ERR>("Exception caught in getUserServiceName."); ++ userMgmtService.clear(); ++ } ++ } ++ return userMgmtService; ++} ++ ++void Manager::initSupportedPrivilges() ++{ ++ std::string userServiceName = getUserServiceName(); ++ if (!userServiceName.empty()) ++ { ++ auto method = bus.new_method_call( ++ getUserServiceName().c_str(), userMgrObjBasePath, ++ "org.freedesktop.DBus.Properties", "Get"); ++ method.append(userMgrInterface, propNameAllPrivileges); ++ ++ auto reply = bus.call(method); ++ if (reply.is_method_error()) ++ { ++ log<level::DEBUG>("get-property AllPrivileges failed", ++ entry("OBJPATH:%s", userMgrObjBasePath), ++ entry("INTERFACE:%s", userMgrInterface)); ++ return; ++ } ++ ++ sdbusplus::message::variant<std::vector<std::string>> result; ++ reply.read(result); ++ ++ supportedPrivList = ++ sdbusplus::message::variant_ns::get<std::vector<std::string>>( ++ result); ++ } ++ ++ // Resgister the signal ++ if (usrMgmtSignal == nullptr) ++ { ++ log<level::DEBUG>("Registering User.Manager propertychange signal."); ++ usrMgmtSignal = std::make_unique<sdbusplus::bus::match_t>( ++ bus, ++ sdbusplus::bus::match::rules::propertiesChanged(userMgrObjBasePath, ++ userMgrInterface), ++ [&](sdbusplus::message::message& msg) { ++ log<level::DEBUG>("UserMgr properties changed signal"); ++ std::map<std::string, DbusVariant> props; ++ std::string iface; ++ msg.read(iface, props); ++ for (const auto& t : props) ++ { ++ if (t.first == propNameAllPrivileges) ++ { ++ supportedPrivList = sdbusplus::message::variant_ns::get< ++ std::vector<std::string>>(t.second); ++ } ++ } ++ }); ++ } ++ return; + } + + bool Manager::createDefaultNetworkFiles(bool force) +diff --git a/network_manager.hpp b/network_manager.hpp +index e2dfea9..22eef04 100644 +--- a/network_manager.hpp ++++ b/network_manager.hpp +@@ -137,6 +137,9 @@ class Manager : public details::VLANCreateIface + return (interfaces.find(intf) != interfaces.end()); + } + ++ /** supported privilege list **/ ++ std::vector<std::string> supportedPrivList; ++ + protected: + /** @brief Persistent sdbusplus DBus bus connection. */ + sdbusplus::bus::bus& bus; +@@ -159,6 +162,12 @@ class Manager : public details::VLANCreateIface + + /** @brief Network Configuration directory. */ + fs::path confDir; ++ ++ /** Get the user management service name dynamically **/ ++ std::string getUserServiceName(); ++ ++ /** @brief initializes the supportedPrivilege List */ ++ void initSupportedPrivilges(); + }; + + } // namespace network +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-network/network/network_%.bbappend b/meta-openbmc-mods/meta-common/recipes-network/network/network_%.bbappend new file mode 100644 index 000000000..275051e19 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-network/network/network_%.bbappend @@ -0,0 +1,9 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +DEPENDS += "nlohmann-json" + +SRC_URI += "file://0001-Patch-to-keep-consistent-MAC-and-IP-address-inbetwee.patch \ + file://0002-IPv6-Network-changes-to-configuration-file.patch \ + file://0003-Adding-channel-specific-privilege-to-network.patch \ + " + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/xyz.openbmc_project.CloseMuxes.service b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/xyz.openbmc_project.CloseMuxes.service new file mode 100644 index 000000000..bc05fa1a1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager/xyz.openbmc_project.CloseMuxes.service @@ -0,0 +1,10 @@ +[Unit] +Description=Close Muxes +ConditionFileNotEmpty=/usr/bin/CloseMuxes.py + +[Service] +ExecStart=/usr/bin/env CloseMuxes.py +Type=oneshot + +[Install] +WantedBy=basic.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager_%.bbappend new file mode 100644 index 000000000..2ae5eeb64 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/configuration/entity-manager_%.bbappend @@ -0,0 +1,18 @@ +# this is here just to bump faster than upstream +SRC_URI = "git://github.com/openbmc/entity-manager.git" +SRCREV = "9945ddffc67a9e564a92a3c0bddf2a2d7dc70b97" + +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +RDEPENDS_${PN} += "python" +SRC_URI += " file://xyz.openbmc_project.CloseMuxes.service" +SYSTEMD_SERVICE_${PN} += " xyz.openbmc_project.CloseMuxes.service" + +EXTRA_OECMAKE = "-DYOCTO=1 -DUSE_OVERLAYS=0" + +do_install_prepend() { + install -d ${D}${bindir} + install -m 0755 ${S}/scripts/CloseMuxes.py ${D}${bindir} + install -d ${D}${base_libdir}/systemd/system + install -m 0644 ${WORKDIR}/xyz.openbmc_project.CloseMuxes.service ${D}${base_libdir}/systemd/system +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0002-Modify-Dbus-for-IPv6.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0002-Modify-Dbus-for-IPv6.patch new file mode 100644 index 000000000..5b86d3154 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0002-Modify-Dbus-for-IPv6.patch @@ -0,0 +1,55 @@ +From 066ecddebc29a87b05f8c66491eec19bb27d1d33 Mon Sep 17 00:00:00 2001 +From: David Cobbley <david.j.cobbley@linux.intel.com> +Date: Wed, 6 Jun 2018 10:11:58 -0700 +Subject: [PATCH 3/3] Modify Dbus for IPv6. + +Add additional interfaces for IPv6 use. +--- + .../Network/EthernetInterface.interface.yaml | 18 ++++++++++++++++++ + xyz/openbmc_project/Network/IP.interface.yaml | 4 ++++ + 2 files changed, 22 insertions(+) + +diff --git a/xyz/openbmc_project/Network/EthernetInterface.interface.yaml b/xyz/openbmc_project/Network/EthernetInterface.interface.yaml +index fc744fc..fd19e27 100644 +--- a/xyz/openbmc_project/Network/EthernetInterface.interface.yaml ++++ b/xyz/openbmc_project/Network/EthernetInterface.interface.yaml +@@ -37,3 +37,21 @@ properties: + Implementation of this Dbus-interface is required to implement this property. + This property supports read/write operation. + Configure the NTP servers on the system during write operation. ++ - name: IPv6AcceptRA ++ type: boolean ++ description: > ++ Boolean for accepting router advertisements in IPv6 ++ - name: IPAddressEnables ++ type: enum[self.IPAllowed] ++ description: > ++ The type of IP connection is allowed on this channel ++ ++enumerations: ++ - name: IPAllowed ++ description: > ++ Determines whether the system allows both IPv6 & IPv4, or disables on ++ or the other ++ values: ++ - name: IPv4AndIPv6 ++ - name: IPv4Only ++ - name: IPv6Only +diff --git a/xyz/openbmc_project/Network/IP.interface.yaml b/xyz/openbmc_project/Network/IP.interface.yaml +index 565dcd6..2ffb016 100644 +--- a/xyz/openbmc_project/Network/IP.interface.yaml ++++ b/xyz/openbmc_project/Network/IP.interface.yaml +@@ -22,6 +22,10 @@ properties: + type: string + description: > + This is the IP gateway for this address. ++ - name: BackupGateway ++ type: string ++ description: > ++ This is the IP address of the backup gateway. + - name: Type + type: enum[self.Protocol] + description: > +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0003-Chassis-Power-Control-are-implemented.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0003-Chassis-Power-Control-are-implemented.patch new file mode 100644 index 000000000..79d02ca9b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0003-Chassis-Power-Control-are-implemented.patch @@ -0,0 +1,257 @@ +From 35271230690c5d85dc7a6502031b38d93ddd683f Mon Sep 17 00:00:00 2001 +From: Ed Tanous <ed.tanous@intel.com> +Date: Thu, 24 Jan 2019 09:29:01 -0800 +Subject: [PATCH] Chassis Power Control are implemented. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Feature level: +Power on server is ready +Power off server is ready +Power cycle server is ready +Power reset server is ready + +Framework level: +WebUI is enabled. +IPMI Commands is enabled. +Restful API is enabled. +Physical buttons (Power/Reset/ID) are enabled. +Chassis state manager is enabled. +Host state manager is enabled. + +Enabled IPMI commands: +ipmitool -H <ip_addr> -P <pass_word> -I lanplus chassis status +ipmitool -H <ip_addr> -P <pass_word> -I lanplus chassis power status +ipmitool -H <ip_addr> -P <pass_word> -I lanplus chassis power on +ipmitool -H <ip_addr> -P <pass_word> -I lanplus chassis power off +ipmitool -H <ip_addr> -P <pass_word> -I lanplus chassis power cycle +ipmitool -H <ip_addr> -P <pass_word> -I lanplus chassis power reset + +Enabled Restful APIs: +Login: curl --noproxy <ip_addr> -c cjar -b cjar -k -H "Content-Type: application/json" -X POST https://<ip_addr>/login -d "{\"data\": [ \"root\", \"0penBmc\" ] }“ +Host State: curl --noproxy <ip_addr> -b cjar -k https://<ip_addr>/xyz/openbmc_project/state/host0 +Chassis State: curl --noproxy <ip_addr> -b cjar -k https://<ip_addr>/xyz/openbmc_project/state/chassis0 +Power state: curl --noproxy <ip_addr> -b cjar -k https://<ip_addr>/xyz/openbmc_project/Chassis/Control/Power0 +Power on/off: curl --noproxy <ip_addr> -q1c cjar -b cjar -k -H "Content-Type: application/json" -d '{"data": "xyz.openbmc_project.State.Host.Transition.Off"}' -X PUT https://<ip_addr>/xyz/openbmc_project/state/host0/attr/RequestedHostTransition + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + .../Chassis/Control/Chassis.interface.yaml | 94 +++++++++++++++++++ + .../Chassis/Control/Power.interface.yaml | 85 ++++++++++++++--- + 2 files changed, 166 insertions(+), 13 deletions(-) + create mode 100644 xyz/openbmc_project/Chassis/Control/Chassis.interface.yaml + +diff --git a/xyz/openbmc_project/Chassis/Control/Chassis.interface.yaml b/xyz/openbmc_project/Chassis/Control/Chassis.interface.yaml +new file mode 100644 +index 0000000..c28492a +--- /dev/null ++++ b/xyz/openbmc_project/Chassis/Control/Chassis.interface.yaml +@@ -0,0 +1,94 @@ ++description: > ++ chassis control service ++methods: ++ - name: powerOn ++ description: > ++ Power on system. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: powerOff ++ description: > ++ Power Off system. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: softPowerOff ++ description: > ++ Soft Power off system. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: reboot ++ description: > ++ reboot system. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: softReboot ++ description: > ++ Soft Reboot system. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: quiesce ++ description: > ++ Quiesce system. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: getPowerState ++ description: > ++ Get system power state. ++ returns: ++ - name: state ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ +diff --git a/xyz/openbmc_project/Chassis/Control/Power.interface.yaml b/xyz/openbmc_project/Chassis/Control/Power.interface.yaml +index 082586f..e77598b 100644 +--- a/xyz/openbmc_project/Chassis/Control/Power.interface.yaml ++++ b/xyz/openbmc_project/Chassis/Control/Power.interface.yaml +@@ -1,31 +1,90 @@ + description: > +- Power control service ++ Chassis control service + methods: ++ - name: setPowerState ++ description: > ++ set host power state. ++ parameters: ++ - name: state ++ type: int32 ++ description: > ++ 0 for force power off host ++ 1 for power on host ++ returns: ++ - name: status ++ type: int32 ++ description: > ++ The result of command. ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ ++ - name: getPowerState ++ description: > ++ Get current host power status. ++ returns: ++ - name: status ++ type: int32 ++ description: > ++ Current host status, ++ 0 for host power off ++ 1 for host power on ++ errors: ++ - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ ++ - xyz.openbmc_project.Chassis.Common.Error.IOError ++ + - name: forcePowerOff + description: > + Force power off the host. + returns: + - name: status +- type: boolean ++ type: int32 + description: > + The result of power off command. + errors: + - xyz.openbmc_project.Chassis.Common.Error.UnsupportedCommand ++ + - xyz.openbmc_project.Chassis.Common.Error.IOError + + properties: +- - name: PGood +- type: boolean +- default: false ++ - name: vrd_good ++ type: int32 ++ default: 0 + description: > +- PSU Power good property +- It is a read-only property. +- - name: State ++ ACPI status ++ - name: s4s5_state + type: int32 + default: 0 + description: > +- System power status +- 0: power is off +- 1: power is on +- Setting its value to change the system state +- Read its value to get the system state. +\ No newline at end of file ++ ACPI status ++ - name: pgood ++ type: int32 ++ default: 0 ++ description: > ++ pgood property ++ - name: state ++ type: int32 ++ default: 0 ++ description: > ++ state property ++ - name: pgood_timeout ++ type: int32 ++ default: 0 ++ description: > ++ pgoodtimeout property ++ - name: post_complete ++ type: boolean ++ default: false ++ description: > ++ The current BIOS POST state, ++ false means not completed or system reset, ++ true means BIOS POST completed. ++signals: ++ - name: PowerGood ++ description: > ++ Signal for powergood ++ - name: PowerLost ++ description: > ++ Signal for powerlost +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0005-Add-DBUS-interface-of-CPU-and-Memory-s-properties.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0005-Add-DBUS-interface-of-CPU-and-Memory-s-properties.patch new file mode 100644 index 000000000..7568f8ce9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0005-Add-DBUS-interface-of-CPU-and-Memory-s-properties.patch @@ -0,0 +1,120 @@ +From a30a09f58b9ebfb267c0b9cce9ae25994ea025ca Mon Sep 17 00:00:00 2001 +From: cyang29 <cheng.c.yang@intel.com> +Date: Tue, 17 Jul 2018 16:04:58 +0800 +Subject: [PATCH] Add DBUS interface of CPU and Memory's properties Feature + Support: SMBIOS service interface. CPU DIMM information redfish + interface. Base on smbios spec DSP0134_3.0.0 + +Signed-off-by: cyang29 <cheng.c.yang@intel.com> +--- + .../Inventory/Item/Cpu.interface.yaml | 41 +++++++++++++++++++ + .../Inventory/Item/Dimm.interface.yaml | 46 +++++++++++++++++++++- + 2 files changed, 86 insertions(+), 1 deletion(-) + +diff --git a/xyz/openbmc_project/Inventory/Item/Cpu.interface.yaml b/xyz/openbmc_project/Inventory/Item/Cpu.interface.yaml +index ab29cf3..313eada 100644 +--- a/xyz/openbmc_project/Inventory/Item/Cpu.interface.yaml ++++ b/xyz/openbmc_project/Inventory/Item/Cpu.interface.yaml +@@ -1,4 +1,45 @@ + description: > + Implement to provide CPU attributes. ++properties: ++ - name: ProcessorSocket ++ type: string ++ description: > ++ Processor Socket on MotherBoard ++ - name: ProcessorType ++ type: string ++ description: > ++ Processor Type of CPU ++ - name: ProcessorFamily ++ type: string ++ description: > ++ Processor Family of CPU ++ - name: ProcessorManufacturer ++ type: string ++ description: > ++ Processor Manufacturer of CPU ++ - name: ProcessorId ++ type: uint32 ++ description: > ++ Processor ID of CPU ++ - name: ProcessorVersion ++ type: string ++ description: > ++ Processor Version of CPU ++ - name: ProcessorMaxSpeed ++ type: uint16 ++ description: > ++ Max Speed CPU Can Support ++ - name: ProcessorCharacteristics ++ type: string ++ description: > ++ The Characteristics CPU Has ++ - name: ProcessorCoreCount ++ type: uint16 ++ description: > ++ The Count of Core in CPU ++ - name: ProcessorThreadCount ++ type: uint16 ++ description: > ++ The Count of Thread CPU Can Support + + # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 +diff --git a/xyz/openbmc_project/Inventory/Item/Dimm.interface.yaml b/xyz/openbmc_project/Inventory/Item/Dimm.interface.yaml +index d85326d..b750320 100644 +--- a/xyz/openbmc_project/Inventory/Item/Dimm.interface.yaml ++++ b/xyz/openbmc_project/Inventory/Item/Dimm.interface.yaml +@@ -1,4 +1,48 @@ + description: > + Implement to provide DIMM attributes. +- ++properties: ++ - name: MemoryDataWidth ++ type: uint16 ++ description: > ++ Data width of Memory. ++ - name: MemorySizeInKB ++ type: uint32 ++ description: > ++ Memory size of DIMM in Kilobyte. ++ - name: MemoryDeviceLocator ++ type: string ++ description: > ++ Socket on base board where Memory located. ++ - name: MemoryType ++ type: string ++ description: > ++ Type of memory. ++ - name: MemoryTypeDetail ++ type: string ++ description: > ++ Additional detail on Memory. ++ - name: MemorySpeed ++ type: uint16 ++ description: > ++ The maximun capable speed of Memory. ++ - name: MemoryManufacturer ++ type: string ++ description: > ++ Manufacturer of memory. ++ - name: MemorySerialNum ++ type: string ++ description: > ++ Memory Serial Number. ++ - name: MemoryPartNum ++ type: string ++ description: > ++ Memory Part Number. ++ - name: MemoryAttributes ++ type: byte ++ description: > ++ Rank attributes of Memory. ++ - name: MemoryConfClockSpeed ++ type: uint16 ++ description: > ++ Configured clock speed to Memory. + # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0006-dbus-interface-add-boot-option-support-for-floppy-an.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0006-dbus-interface-add-boot-option-support-for-floppy-an.patch new file mode 100644 index 000000000..f0d7b03f8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0006-dbus-interface-add-boot-option-support-for-floppy-an.patch @@ -0,0 +1,77 @@ +From 643772fc7f6021fbfba3b14de0c86501ae3e7f3a Mon Sep 17 00:00:00 2001 +From: "Jia, Chunhui" <chunhui.jia@intel.com> +Date: Fri, 13 Jul 2018 15:22:05 +0800 +Subject: [PATCH] [dbus interface]add boot option support for floppy and USB + +Current implementations use ExternalMedia type to specify both CD/DVD/USB +/Floppy. But in IPMI spec, they are different. CD/DVD type is 0x5 and +USB/Floppy type is 0xF. + +This causes a bug that we can not force BIOS boots into USB/Floppy. + +Test: +$ ipmitool -H 10.239.56.91 -P 0penBmc -I lanplus raw + 0x0 0x8 0x5 0x80 0x14 0x00 0x00 0x00 +$ ipmitool -H 10.239.56.91 -P 0penBmc -I lanplus chassis bootparam get 5 +Boot parameter version: 1 +Boot parameter 5 is valid/unlocked +Boot parameter data: 8014000000 + Boot Flags : + - Boot Flag Valid + - Options apply to only next boot + - BIOS PC Compatible (legacy) boot + - Boot Device Selector : Force Boot from CD/DVD + - Console Redirection control : System Default + - BIOS verbosity : Console redirection occurs per BIOS + configuration setting (default) + - BIOS Mux Control Override : + BIOS uses recommended setting of the mux at the end of POST + +$ipmitool -H 10.239.56.91 -P 0penBmc -I lanplus raw + 0x0 0x8 0x5 0x80 0x3c 0x00 0x00 0x00 +$ipmitool -H 10.239.56.91 -P 0penBmc -I lanplus chassis bootparam get 5 +Boot parameter version: 1 +Boot parameter 5 is valid/unlocked +Boot parameter data: 803c000000 + Boot Flags : + - Boot Flag Valid + - Options apply to only next boot + - BIOS PC Compatible (legacy) boot + - Boot Device Selector : Force Boot from Floppy/primary removable media + - Console Redirection control : System Default + - BIOS verbosity : + Console redirection occurs per BIOS configuration setting (default) + + - BIOS Mux Control Override : + BIOS uses recommended setting of the mux at the end of POST + +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +--- + xyz/openbmc_project/Control/Boot/Source.interface.yaml | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/xyz/openbmc_project/Control/Boot/Source.interface.yaml b/xyz/openbmc_project/Control/Boot/Source.interface.yaml +index ea811bd..8e5916f 100644 +--- a/xyz/openbmc_project/Control/Boot/Source.interface.yaml ++++ b/xyz/openbmc_project/Control/Boot/Source.interface.yaml +@@ -15,12 +15,15 @@ enumerations: + - name: Disk + description: > + Boot from the local hard disk. +- - name: ExternalMedia ++ - name: DVD + description: > +- Boot from CD/DVD/USB, etc. ++ Boot from CD/DVD. + - name: Network + description: > + Boot from a remote source over a network. + - name: Default + description: > + Boot from an implementation defined source. ++ - name: Removable ++ description: > ++ Boot from floppy/primary removable media(USB). +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0007-ipmi-set-BIOS-id.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0007-ipmi-set-BIOS-id.patch new file mode 100644 index 000000000..c87b2d89d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0007-ipmi-set-BIOS-id.patch @@ -0,0 +1,32 @@ +From 49debd0955b672d591f35e74119b288bd6df2992 Mon Sep 17 00:00:00 2001 +From: "Jia, Chunhui" <chunhui.jia@intel.com> +Date: Tue, 24 Jul 2018 11:40:49 +0800 +Subject: [PATCH] [ipmi] set BIOS id + +change#2 +add new dbus interface for BIOS attributes + +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +--- + xyz/openbmc_project/Inventory/Item/Bios.interface.yaml | 9 +++++++++ + 1 file changed, 9 insertions(+) + create mode 100644 xyz/openbmc_project/Inventory/Item/Bios.interface.yaml + +diff --git a/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml b/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml +new file mode 100644 +index 0000000..d7a6b95 +--- /dev/null ++++ b/xyz/openbmc_project/Inventory/Item/Bios.interface.yaml +@@ -0,0 +1,9 @@ ++description: > ++ Implement to provide BIOS attributes. ++properties: ++ - name: BiosId ++ type: string ++ description: > ++ BIOS ID (version) string ++ ++# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0009-Add-host-restart-cause-property.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0009-Add-host-restart-cause-property.patch new file mode 100644 index 000000000..1221a0ab4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0009-Add-host-restart-cause-property.patch @@ -0,0 +1,98 @@ +From 72b7b30a5dda56c170ee2ce82c1082c26f4663e3 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Tue, 31 Jul 2018 16:55:21 +0800 +Subject: [PATCH] Add host restart cause property. + +Add host restart cause property to track the information about what +action last caused the system to restart. +According to IPMI Spec, it includes 12 types as following: +1. Unknown 0x0 +2. IpmiCommand 0x1 +3. ResetButton 0x2 +4. PowerButton 0x3 +5. WatchdogTimer 0x4 +6. OEM 0x5 +7. PowerPolicyAlwaysOn 0x6 +8. PowerPolicyPreviousState 0x7 +9. PEF-Reset 0x8 +10. PEF-PowerCycle 0x9 +11. SoftReset 0xA +12. RTC-Wakeup 0xB + +Change-Id: Id2cc6a18b98e485a978940e5ffc085bf5c4fbed8 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + xyz/openbmc_project/State/Host.interface.yaml | 52 +++++++++++++++++++ + 1 file changed, 52 insertions(+) + +diff --git a/xyz/openbmc_project/State/Host.interface.yaml b/xyz/openbmc_project/State/Host.interface.yaml +index 8f481b8..a4d88d0 100644 +--- a/xyz/openbmc_project/State/Host.interface.yaml ++++ b/xyz/openbmc_project/State/Host.interface.yaml +@@ -17,6 +17,12 @@ properties: + comparing the CurrentHostState and the RequestedHostTransition + properties. + ++ - name: HostRestartCause ++ type: enum[self.RestartCause] ++ default: Unknown ++ description: > ++ The information about what action last caused the system to restart. ++ + enumerations: + - name: Transition + description: > +@@ -45,3 +51,49 @@ enumerations: + - name: 'Quiesced' + description: > + Host firmware is quiesced ++ ++ - name: RestartCause ++ description: > ++ The information about what action last caused the system to restart. ++ values: ++ - name: Unknown ++ description: > ++ System start/restart detected but the reason is unknown. ++ - name: IpmiCommand ++ description: > ++ System start/restart detected and caused by ipmi command. ++ - name: ResetButton ++ description: > ++ System start/restart detected and caused by reset button. ++ - name: PowerButton ++ description: > ++ System start/restart detected and caused by power button. ++ - name: WatchdogTimer ++ description: > ++ System start/restart detected and casued by watchdog expiration. ++ - name: OEM ++ description: > ++ System start/restart detected and caused by OEM command. ++ - name: PowerPolicyAlwaysOn ++ description: > ++ System start/restart detected and caused by power restore policy ++ "chassis always powers up after AC/mains is applied or returns". ++ - name: PowerPolicyPreviousState ++ description: > ++ System start/restart detected and caused by power restore policy ++ "after AC/mains is applied or returns, power is restored to the ++ state was in effect when AC/mains removed or lost". ++ - name: PEFReset ++ description: > ++ System start/restart detected and caused by PEF(reset). ++ - name: PEFPowerCycle ++ description: > ++ System start/restart detected and caused by PEF(power-cycle). ++ - name: SoftReset ++ description: > ++ System start/restart detected and caused by soft reset like ++ "CTRL-ALT-DEL". ++ - name: RTCWakeup ++ description: > ++ System start/restart detected and caused by system real time ++ clock(RTC) wakeup. +\ No newline at end of file +-- +2.17.0 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0010-Increase-the-default-watchdog-timeout-value.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0010-Increase-the-default-watchdog-timeout-value.patch new file mode 100644 index 000000000..2c9344306 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0010-Increase-the-default-watchdog-timeout-value.patch @@ -0,0 +1,34 @@ +From 631deef0ca88a77283741edeae8078d2185f414c Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Fri, 10 Aug 2018 16:23:13 +0800 +Subject: [PATCH] Increase the default watchdog timeout value + +The default timeout for poweron is 30 seconds, +but currently the host power on needs 120+ seconds +due to unimplemented ipmi commands for BIOS. + +Increase the value as a workaround, +to avoid the watchdog timeout during power on. +Will adjust this value in the future + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + xyz/openbmc_project/State/Watchdog.interface.yaml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xyz/openbmc_project/State/Watchdog.interface.yaml b/xyz/openbmc_project/State/Watchdog.interface.yaml +index f76dbf2..402e1a8 100644 +--- a/xyz/openbmc_project/State/Watchdog.interface.yaml ++++ b/xyz/openbmc_project/State/Watchdog.interface.yaml +@@ -37,7 +37,7 @@ properties: + type: uint64 + description: > + Time interval to arm the watchdog, in milli-second. +- default: 30000 ++ default: 600000 + - name: TimeRemaining + type: uint64 + description: > +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0012-Add-RestoreDelay-interface-for-power-restore-delay.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0012-Add-RestoreDelay-interface-for-power-restore-delay.patch new file mode 100644 index 000000000..9052435ca --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0012-Add-RestoreDelay-interface-for-power-restore-delay.patch @@ -0,0 +1,34 @@ +From eeac4cf4528994aeb213d549daf4c033ac9d3bbc Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Fri, 24 Aug 2018 17:55:35 +0800 +Subject: [PATCH] Add RestoreDelay interface for power restore delay + +Which provide one property "PowerRestoreDelay" + +Change-Id: I4e6d3e45948b1e288301b4aa52cc08cace4f1bc2 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + .../Control/Power/RestoreDelay.interface.yaml | 11 +++++++++++ + 1 file changed, 11 insertions(+) + create mode 100644 xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml + +diff --git a/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml b/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml +new file mode 100644 +index 0000000..55ee80a +--- /dev/null ++++ b/xyz/openbmc_project/Control/Power/RestoreDelay.interface.yaml +@@ -0,0 +1,11 @@ ++description: > ++ Implement to specify power transition behavior on a BMC reset. ++ The implementation based on restore policy and set a delay time ++ for power restore. ++ ++properties: ++ - name: PowerRestoreDelay ++ type: uint16 ++ description: > ++ The delay time for power restore. ++ Power Restore Delay is NOT applied on power policy is "Always Off" +-- +2.17.0 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0013-Add-ErrConfig.yaml-interface-for-processor-error-config.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0013-Add-ErrConfig.yaml-interface-for-processor-error-config.patch new file mode 100644 index 000000000..9471c7ab2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0013-Add-ErrConfig.yaml-interface-for-processor-error-config.patch @@ -0,0 +1,86 @@ +From 7260c24b201759f3a5168eebfee215072c13e641 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Thu, 30 Aug 2018 16:22:43 +0800 +Subject: [PATCH] Add ErrConfig.yaml interface for processor error + configuration. + +Which provide 3 properties: + ResetCfg + type: byte + description: > + Reset Configuration + [0]: CATERR Reset Enabled + 0b: Disabled + 1b: Enabled + [1]: ERR2 Reset Enabled + 0b: Disabled + 1b: Enabled + [7:2]: Reserved + ResetErrorOccurrenceCounts + type: byte + description: > + Reset Error Occurrence Counts + [0]: Reset CPU Error Counts + 0b: Keep CPU Error Counts + 1b: Reset all CPU Error Counts to zero + [7:1]: Reserved + CATERRStatus + type: array[byte] + description: > + For all CPUs including the non-legacy socket CPU + CPU CATERR (Core Error) occurrence + [5:0]: Error Occurrence Count + [7:6]: CPU Status + 00b: Disabled + 01b: Enabled + 11b: Not Present + +Change-Id: Ibc5a7a5e15c998e56c04e23b1043d99243a91171 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + .../Processor/ErrConfig.interface.yaml | 33 +++++++++++++++++++ + 1 file changed, 33 insertions(+) + create mode 100644 xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml + +diff --git a/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml b/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml +new file mode 100644 +index 0000000..2304263 +--- /dev/null ++++ b/xyz/openbmc_project/Control/Processor/ErrConfig.interface.yaml +@@ -0,0 +1,33 @@ ++description: > ++ This defines processor error configuration. ++properties: ++ - name: ResetCfg ++ type: byte ++ description: > ++ Reset Configuration ++ [0]: CATERR Reset Enabled ++ 0b: Disabled ++ 1b: Enabled ++ [1]: ERR2 Reset Enabled ++ 0b: Disabled ++ 1b: Enabled ++ [7:2]: Reserved ++ ++ - name: ResetErrorOccurrenceCounts ++ type: byte ++ description: > ++ Reset Error Occurrence Counts ++ [0]: Reset CPU Error Counts ++ 0b: Keep CPU Error Counts ++ 1b: Reset all CPU Error Counts to zero ++ [7:1]: Reserved ++ - name: CATERRStatus ++ type: array[byte] ++ description: > ++ For all CPUs including the non-legacy socket CPU ++ CPU CATERR (Core Error) occurrence ++ [5:0]: Error Occurrence Count ++ [7:6]: CPU Status ++ 00b: Disabled ++ 01b: Enabled ++ 11b: Not Present +-- +2.17.0 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0014-Add-multiple-state-signal-for-host-start-and-stop.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0014-Add-multiple-state-signal-for-host-start-and-stop.patch new file mode 100644 index 000000000..a8d732dab --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0014-Add-multiple-state-signal-for-host-start-and-stop.patch @@ -0,0 +1,63 @@ +From 6d0069f4a2b4637c58fd321c9db3034ac9dd17c7 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Wed, 12 Sep 2018 15:03:26 +0800 +Subject: [PATCH] Add multiple state signal for host start and stop + +Add six host state signals like following: + - name: PreHostStart + description: > + This is the signal to indicate host is at pre start stage. + - name: PostHostStart + description: > + This is the signal to indicate host is at start complete stage. + - name: HostStarting + description: > + This is the signal to indicate host is at starting stage. + - name: HostStoping + description: > + This is the signal to indicate host is at stoping stage. + - name: PreHostStop + description: > + This is the signal to indicate host is at pre stop stage. + - name: PostHostStop + description: > + This is the signal to indicate host is at stop complete stage. + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + xyz/openbmc_project/State/Host.interface.yaml | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/xyz/openbmc_project/State/Host.interface.yaml b/xyz/openbmc_project/State/Host.interface.yaml +index 8531a27..1ceba13 100644 +--- a/xyz/openbmc_project/State/Host.interface.yaml ++++ b/xyz/openbmc_project/State/Host.interface.yaml +@@ -96,4 +96,23 @@ enumerations: + - name: RTCWakeup + description: > + System start/restart detected and caused by system real time +- clock(RTC) wakeup. +\ No newline at end of file ++ clock(RTC) wakeup. ++signals: ++ - name: PreHostStart ++ description: > ++ This is the signal to indicate host is at pre start stage. ++ - name: PostHostStart ++ description: > ++ This is the signal to indicate host is at start complete stage. ++ - name: HostStarting ++ description: > ++ This is the signal to indicate host is at starting stage. ++ - name: HostStoping ++ description: > ++ This is the signal to indicate host is at stoping stage. ++ - name: PreHostStop ++ description: > ++ This is the signal to indicate host is at pre stop stage. ++ - name: PostHostStop ++ description: > ++ This is the signal to indicate host is at stop complete stage. +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0016-Add-DBUS-interface-of-SMBIOS-MDR-V2.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0016-Add-DBUS-interface-of-SMBIOS-MDR-V2.patch new file mode 100644 index 000000000..576bae81a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0016-Add-DBUS-interface-of-SMBIOS-MDR-V2.patch @@ -0,0 +1,227 @@ +From 9490574667485cd407193ff9f0d6a96f8c2c87d3 Mon Sep 17 00:00:00 2001 +From: cyang29 <cheng.c.yang@intel.com> +Date: Wed, 12 Sep 2018 00:27:23 +0800 +Subject: [PATCH] Add DBUS interface of SMBIOS MDR V2 + +Support: + SMBIOS MDR V2 service interface. + SMBIOS MDR V2 IPMI Command + SMBIOS MDR V2 Redfish interface. +Base on SMBIOS spec DSP0134_3.0.0 and Managed Data Region +Specification Revision 4 +--- + xyz/openbmc_project/Smbios/MDR_V2.errors.yaml | 9 + + xyz/openbmc_project/Smbios/MDR_V2.interface.yaml | 158 +++++++++++++++++++++++ + xyz/openbmc_project/Smbios/README.md | 21 +++ + 3 files changed, 188 insertions(+) + create mode 100644 xyz/openbmc_project/Smbios/MDR_V2.errors.yaml + create mode 100644 xyz/openbmc_project/Smbios/MDR_V2.interface.yaml + create mode 100644 xyz/openbmc_project/Smbios/README.md + +diff --git a/xyz/openbmc_project/Smbios/MDR_V2.errors.yaml b/xyz/openbmc_project/Smbios/MDR_V2.errors.yaml +new file mode 100644 +index 0000000..88bd6db +--- /dev/null ++++ b/xyz/openbmc_project/Smbios/MDR_V2.errors.yaml +@@ -0,0 +1,9 @@ ++- name: InvalidParameter ++ description: > ++ An invalid parameter is attempted. ++- name: UpdateInProgress ++ description: > ++ Update is in progress. ++- name: InvalidId ++ description: > ++ An invalid Id is attempted. +diff --git a/xyz/openbmc_project/Smbios/MDR_V2.interface.yaml b/xyz/openbmc_project/Smbios/MDR_V2.interface.yaml +new file mode 100644 +index 0000000..f97700a +--- /dev/null ++++ b/xyz/openbmc_project/Smbios/MDR_V2.interface.yaml +@@ -0,0 +1,158 @@ ++description: > ++ SMBIOS MDR V2 service ++methods: ++ - name: GetDirectoryInformation ++ description: > ++ Get the directory with directory index. ++ parameters: ++ - name: dirIndex ++ type: byte ++ description: > ++ Directory index of SMBIOS. ++ returns: ++ - name: dir ++ type: array[byte] ++ description: > ++ Directory of agent. ++ errors: ++ - self.Error.InvalidParameter ++ ++ - name: GetDataInformation ++ description: > ++ Get the data info with id index and data set ID. ++ parameters: ++ - name: idIndex ++ type: byte ++ description: > ++ Index of SMBIOS directory. ++ returns: ++ - name: dataInfo ++ type: array[byte] ++ description: > ++ Data information of SMBIOS. ++ errors: ++ - self.Error.InvalidParameter ++ ++ - name: SendDirectoryInformation ++ description: > ++ Send directory information to SMBIOS directory. ++ parameters: ++ - name: dirVersion ++ type: byte ++ description: > ++ A counter which increments each time directory updated. ++ - name: dirIndex ++ type: byte ++ description: > ++ Directory index of SMBIOS. ++ - name: returnedEntries ++ type: byte ++ description: > ++ Indicates number of directory entries. ++ - name: remainingEntries ++ type: byte ++ description: > ++ Remaining entries which are higher than index in this transfer. ++ - name: dirEntry ++ type: array[byte] ++ description: > ++ Data set ID of SMBIOS table. ++ returns: ++ - name: status ++ type: boolean ++ description: > ++ Need to continue directory transmisson or not. ++ errors: ++ - self.Error.InvalidParameter ++ ++ - name: GetDataOffer ++ description: > ++ Get data set ID. ++ returns: ++ - name: offer ++ type: array[byte] ++ description: > ++ Data set ID. ++ errors: ++ - self.Error.UpdateInProgress ++ ++ - name: SendDataInformation ++ description: > ++ Send data information with directory index. ++ parameters: ++ - name: idIndex ++ type: byte ++ description: > ++ Index of SMBIOS directory. ++ - name: flag ++ type: byte ++ description: > ++ Valid flag to set dir entry status. ++ - name: dataLen ++ type: uint32 ++ description: > ++ The length of the data in bytes. ++ - name: dataVer ++ type: uint32 ++ description: > ++ The version number of this data. ++ - name: timeStamp ++ type: uint32 ++ description: > ++ Timestamp determinded by the agent. ++ returns: ++ - name: status ++ type: boolean ++ description: > ++ Whether data changes. ++ errors: ++ - self.Error.InvalidParameter ++ ++ - name: FindIdIndex ++ description: > ++ Find id index by data info. ++ parameters: ++ - name: dataInfo ++ type: array[byte] ++ description: > ++ Data info of data entry. ++ returns: ++ - name: idIndex ++ type: int32 ++ description: > ++ Id index of data entry. ++ errors: ++ - self.Error.InvalidId ++ ++ - name: AgentSynchronizeData ++ description: > ++ Synchronize SMBIOS data from file. ++ returns: ++ - name: status ++ type: boolean ++ description: > ++ Whether synchronization succeed or not. ++ ++ - name: SynchronizeDirectoryCommonData ++ description: > ++ Synchronize directory common data. ++ parameters: ++ - name: idIndex ++ type: byte ++ description: > ++ Index of SMBIOS directory. ++ - name: size ++ type: uint32 ++ description: > ++ Size of data that BIOS prepare to transfer. ++ returns: ++ - name: commonData ++ type: array[uint32] ++ description: > ++ Directory common data includes data size, version and timestamp. ++ ++properties: ++ - name: DirectoryEntries ++ type: byte ++ description: > ++ Numbers of directory entries. +diff --git a/xyz/openbmc_project/Smbios/README.md b/xyz/openbmc_project/Smbios/README.md +new file mode 100644 +index 0000000..415ac52 +--- /dev/null ++++ b/xyz/openbmc_project/Smbios/README.md +@@ -0,0 +1,22 @@ ++# SMBIOS MDR V2 ++ ++## Overview ++SMBIOS MDR V2 service exposes D-Bus methods for SMBIOS Version 2 operations. ++ ++### SMBIOS MDR V2 Interface ++SMBIOS MDR V2 interface `xyz.openbmc_project.Smbios.MDR_V2` provides following ++methods. ++#### methods ++* GetDirectoryInformation - Get the directory with directory index. ++* GetDataInformation - Get the data information with id index and data set ID. ++* SendDirectoryInformation - Send directory information to SMBIOS directory. ++* GetDataOffer - Get data set ID. ++* SendDataInformation - Send data information with directory index. ++* FindIdIndex - Find id index by data info. ++* SynchronizeDirectoryCommonData - Synchronize directory common data before ++SMBIOS data start to transfer. ++* AgentSynchronizeData - Synchronize SMBIOS data from file after data transfer ++complete. ++ ++#### properties ++* DirEntries - Numbers of directory entries. Default: 0 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0017-Add-shutdown-policy-interface-for-get-set-shutdown-p.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0017-Add-shutdown-policy-interface-for-get-set-shutdown-p.patch new file mode 100644 index 000000000..587bcebf1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0017-Add-shutdown-policy-interface-for-get-set-shutdown-p.patch @@ -0,0 +1,42 @@ +From 7ebb72a93922a0163a5b35c277f3bbd241bdf78c Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 22 Oct 2018 16:20:36 +0800 +Subject: [PATCH] Add shutdown policy interface for get/set shutdown policy OEM + IPMI commands + +The policy property is used to store the shutdown policy. + +Tested by: +busctl get-property "xyz.openbmc_project.Settings" \ +"/xyz/openbmc_project/control/shutdown_policy_config" \ +"xyz.openbmc_project.Control.ShutdownPolicy" "Policy" + +busctl set-property "xyz.openbmc_project.Settings" \ +"/xyz/openbmc_project/control/shutdown_policy_config" \ +"xyz.openbmc_project.Control.ShutdownPolicy" "Policy" y 1 + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + xyz/openbmc_project/Control/ShutdownPolicy.interface.yaml | 10 ++++++++++ + 1 file changed, 10 insertions(+) + create mode 100644 xyz/openbmc_project/Control/ShutdownPolicy.interface.yaml + +diff --git a/xyz/openbmc_project/Control/ShutdownPolicy.interface.yaml b/xyz/openbmc_project/Control/ShutdownPolicy.interface.yaml +new file mode 100644 +index 0000000..e562ea8 +--- /dev/null ++++ b/xyz/openbmc_project/Control/ShutdownPolicy.interface.yaml +@@ -0,0 +1,10 @@ ++description: > ++ An interface for node shutdown policy on multi-node products. ++properties: ++ - name: Policy ++ type: byte ++ description: > ++ 0: Do not shutdown node on a power supply over current(OC) ++ or a power supply over temperature(OT) event. ++ 1: Shutdown node on an OC/OT event. ++ Only available on multi-node products. +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0018-Define-post-code-interfaces-for-post-code-manager.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0018-Define-post-code-interfaces-for-post-code-manager.patch new file mode 100644 index 000000000..ce23c222b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces/0018-Define-post-code-interfaces-for-post-code-manager.patch @@ -0,0 +1,64 @@ +From f88cac8364d5312e29208018909827d2da4a0f87 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Tue, 19 Feb 2019 16:52:51 +0800 +Subject: [PATCH] Define post code interfaces for post code manager + +It includes one method and 2 properties. +properties: + - name: CurrentBootCycleIndex + description: > + It is used to indicate current boot cycle index. + - name: MaxBootCycleNum + description: > + The max cached boot cycles for post code. +methods: + - name: GetPostCodes + description: > + Method to get the cached post code for each boot cycle. +TestBy: bitbake build + +Signeoff-by: Kuiying Wang <kuiying.wang@intel.com> +--- + .../State/Boot/PostCode.interface.yaml | 30 ++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + create mode 100644 xyz/openbmc_project/State/Boot/PostCode.interface.yaml + +diff --git a/xyz/openbmc_project/State/Boot/PostCode.interface.yaml b/xyz/openbmc_project/State/Boot/PostCode.interface.yaml +new file mode 100644 +index 0000000..711749d +--- /dev/null ++++ b/xyz/openbmc_project/State/Boot/PostCode.interface.yaml +@@ -0,0 +1,30 @@ ++description: > ++ Monitor Post code coming and buffer all of them based on boot cycle ++ into file system. ++ ++properties: ++ - name: CurrentBootCycleIndex ++ type: uint16 ++ description: > ++ It is used to indicate current boot cycle index. ++ - name: MaxBootCycleNum ++ type: uint16 ++ description: > ++ The max cached boot cycles for post code. ++methods: ++ - name: GetPostCodes ++ description: > ++ Method to get the cached post code for each boot cycle. ++ parameters: ++ - name: Index ++ type: uint16 ++ description: > ++ Index indicates which boot cycle of post codes is requested. ++ returns: ++ - name: codes ++ type: array[uint64] ++ description: > ++ An array of post codes of one boot cycle. ++ errors: ++ - xyz.openbmc_project.Common.Error.InternalFailure ++ - xyz.openbmc_project.Common.Error.InvalidArgument +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces_%.bbappend new file mode 100644 index 000000000..2449d9225 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-dbus-interfaces_%.bbappend @@ -0,0 +1,16 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://0002-Modify-Dbus-for-IPv6.patch \ + file://0003-Chassis-Power-Control-are-implemented.patch \ + file://0005-Add-DBUS-interface-of-CPU-and-Memory-s-properties.patch \ + file://0006-dbus-interface-add-boot-option-support-for-floppy-an.patch \ + file://0007-ipmi-set-BIOS-id.patch \ + file://0009-Add-host-restart-cause-property.patch \ + file://0010-Increase-the-default-watchdog-timeout-value.patch \ + file://0012-Add-RestoreDelay-interface-for-power-restore-delay.patch \ + file://0013-Add-ErrConfig.yaml-interface-for-processor-error-config.patch \ + file://0014-Add-multiple-state-signal-for-host-start-and-stop.patch \ + file://0016-Add-DBUS-interface-of-SMBIOS-MDR-V2.patch \ + file://0017-Add-shutdown-policy-interface-for-get-set-shutdown-p.patch \ + file://0018-Define-post-code-interfaces-for-post-code-manager.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper/xyz.openbmc_project.ObjectMapper.service b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper/xyz.openbmc_project.ObjectMapper.service new file mode 100644 index 000000000..9af9af254 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper/xyz.openbmc_project.ObjectMapper.service @@ -0,0 +1,20 @@ +[Unit] +Description=Phosphor DBus Service Discovery Manager +Before=obmc-mapper.target +After=dbus.socket + +[Service] +Restart=always +Type=dbus +ExecStart=/usr/bin/env mapperx \ + --service-namespaces="xyz. com. org." \ + --interface-namespaces="org. com. xyz." \ + --service-blacklists="org.freedesktop.systemd1" +SyslogIdentifier=phosphor-mapper +BusName={BUSNAME} +TimeoutStartSec=300 +RestartSec=5 +EnvironmentFile={envfiledir}/obmc/mapper + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper_%.bbappend new file mode 100644 index 000000000..72d991c7e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/dbus/phosphor-mapper_%.bbappend @@ -0,0 +1 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control/phosphor-pid-control.service b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control/phosphor-pid-control.service new file mode 100644 index 000000000..f4ffa17a0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control/phosphor-pid-control.service @@ -0,0 +1,12 @@ +[Unit] +Description=Phosphor-Pid-Control Margin-based Fan Control Daemon + +[Service] +Restart=always +ExecStart={sbindir}/swampd +RestartSec=5 +StartLimitInterval=0 +Type=simple + +[Install] +WantedBy=basic.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend new file mode 100644 index 000000000..3ea7b96b1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend @@ -0,0 +1,8 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +inherit obmc-phosphor-systemd +SYSTEMD_SERVICE_${PN} = "phosphor-pid-control.service" +EXTRA_OECONF = "--enable-configure-dbus=yes" + +SRC_URI = "git://github.com/openbmc/phosphor-pid-control.git" +SRCREV = "f42741197ec807e0436a5e519ccff18519c67248" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0001-image_verify-Add-support-for-OpenSSL-1.1.0.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0001-image_verify-Add-support-for-OpenSSL-1.1.0.patch new file mode 100644 index 000000000..c5850473c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/0001-image_verify-Add-support-for-OpenSSL-1.1.0.patch @@ -0,0 +1,130 @@ +From fa124c7944088624d40d6b265bac0651bd8235bb Mon Sep 17 00:00:00 2001 +From: Adriana Kobylak <anoo@us.ibm.com> +Date: Thu, 6 Sep 2018 13:15:34 -0500 +Subject: [PATCH] image_verify: Add support for OpenSSL 1.1.0 + +With OpenSSL 1.1.0, some of the functions were renamed, for +example EVP_MD_CTX_create() and EVP_MD_CTX_destroy() were +renamed to EVP_MD_CTX_new() and EVP_MD_CTX_free(). +Reference: https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes +Abstract them to support old and new APIs. + +Resolves openbmc/openbmc#3136 + +Tested: Verified the signature verification was successful. + +Change-Id: I2297243fdd652055fe9ea88f26eb2dcf473d24e6 +Signed-off-by: Adriana Kobylak <anoo@us.ibm.com> + +%% original patch: 0001-image_verify-Add-support-for-OpenSSL-1.1.0.patch +--- + Makefile.am | 8 ++++++-- + image_verify.cpp | 2 +- + image_verify.hpp | 1 + + utils.cpp | 29 +++++++++++++++++++++++++++++ + utils.hpp | 15 +++++++++++++++ + 5 files changed, 52 insertions(+), 3 deletions(-) + create mode 100644 utils.cpp + create mode 100644 utils.hpp + +diff --git a/Makefile.am b/Makefile.am +index adba0e4..21b556f 100755 +--- a/Makefile.am ++++ b/Makefile.am +@@ -42,8 +42,12 @@ phosphor_image_updater_SOURCES = \ + include ubi/Makefile.am.include + + if WANT_SIGNATURE_VERIFY_BUILD +-noinst_HEADERS += image_verify.hpp +-phosphor_image_updater_SOURCES += image_verify.cpp ++noinst_HEADERS += \ ++ image_verify.hpp \ ++ utils.hpp ++phosphor_image_updater_SOURCES += \ ++ image_verify.cpp \ ++ utils.cpp + endif + + if WANT_SYNC +diff --git a/image_verify.cpp b/image_verify.cpp +index 7d59910..ba6b24d 100644 +--- a/image_verify.cpp ++++ b/image_verify.cpp +@@ -216,7 +216,7 @@ bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile, + EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA); + + // Initializes a digest context. +- EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_create(), ::EVP_MD_CTX_destroy); ++ EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free); + + // Adds all digest algorithms to the internal table + OpenSSL_add_all_digests(); +diff --git a/image_verify.hpp b/image_verify.hpp +index cbd0e39..22ee5f9 100644 +--- a/image_verify.hpp ++++ b/image_verify.hpp +@@ -1,4 +1,5 @@ + #pragma once ++#include "utils.hpp" + #include <openssl/rsa.h> + #include <openssl/evp.h> + #include <openssl/pem.h> +diff --git a/utils.cpp b/utils.cpp +new file mode 100644 +index 0000000..95fc2e0 +--- /dev/null ++++ b/utils.cpp +@@ -0,0 +1,29 @@ ++#include "utils.hpp" ++ ++#if OPENSSL_VERSION_NUMBER < 0x10100000L ++ ++#include <string.h> ++ ++static void* OPENSSL_zalloc(size_t num) ++{ ++ void* ret = OPENSSL_malloc(num); ++ ++ if (ret != NULL) ++ { ++ memset(ret, 0, num); ++ } ++ return ret; ++} ++ ++EVP_MD_CTX* EVP_MD_CTX_new(void) ++{ ++ return (EVP_MD_CTX*)OPENSSL_zalloc(sizeof(EVP_MD_CTX)); ++} ++ ++void EVP_MD_CTX_free(EVP_MD_CTX* ctx) ++{ ++ EVP_MD_CTX_cleanup(ctx); ++ OPENSSL_free(ctx); ++} ++ ++#endif // OPENSSL_VERSION_NUMBER < 0x10100000L +diff --git a/utils.hpp b/utils.hpp +new file mode 100644 +index 0000000..90569bf +--- /dev/null ++++ b/utils.hpp +@@ -0,0 +1,15 @@ ++#pragma once ++ ++// With OpenSSL 1.1.0, some functions were deprecated. Need to abstract them ++// to make the code backward compatible with older OpenSSL veresions. ++// Reference: https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes ++#if OPENSSL_VERSION_NUMBER < 0x10100000L ++ ++#include <openssl/evp.h> ++ ++extern "C" { ++EVP_MD_CTX* EVP_MD_CTX_new(void); ++void EVP_MD_CTX_free(EVP_MD_CTX* ctx); ++} ++ ++#endif // OPENSSL_VERSION_NUMBER < 0x10100000L +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/fwupd@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/fwupd@.service new file mode 100644 index 000000000..d51fee312 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager/fwupd@.service @@ -0,0 +1,8 @@ +[Unit]
+Description=Flash BMC with fwupd script : %I
+
+[Service]
+Type=oneshot
+RemainAfterExit=no
+ExecStart=/usr/bin/fwupd.sh file:////tmp/images/%i/image-runtime
+SyslogIdentifier=fwupd
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager_%.bbappend new file mode 100644 index 000000000..9c3c3ee37 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/flash/phosphor-software-manager_%.bbappend @@ -0,0 +1,11 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +EXTRA_OECONF += "--enable-fwupd_script" + +SYSTEMD_SERVICE_${PN}-updater += "fwupd@.service" + +SRC_URI_remove = "git://github.com/openbmc/phosphor-bmc-code-mgmt" +SRC_URI += "git://git-amr-2.devtools.intel.com:29418/openbmc-phosphor-bmc-code-mgmt;protocol=ssh" +SRCREV = "f8f76c29dbe2806a6eacd15847563cdf7f7567f4" + +#Currently enforcing image signature validation only for PFR images +PACKAGECONFIG_append = "${@bb.utils.contains('IMAGE_TYPE', 'pfr', ' verify_signature', '', d)}" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/gpiodaemon/gpiodaemon.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/gpiodaemon/gpiodaemon.bb new file mode 100644 index 000000000..6d6762fc9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/gpiodaemon/gpiodaemon.bb @@ -0,0 +1,18 @@ +SUMMARY = "Gpio daemon service for handling gpio operations" +DESCRIPTION = "Daemon allows to block gpio access under certain conditions" + +PV = "1.0+git${SRCPV}" + +S = "${WORKDIR}/git/gpiodaemon" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SRC_URI = "git://git@github.com/Intel-BMC/provingground.git;protocol=ssh" + +SRCREV = "3cc86d6c536b4c5ee7afb5447837b83ce8b3d149" + +inherit cmake systemd +SYSTEMD_SERVICE_${PN} = "gpiodaemon.service" + +DEPENDS = "boost systemd sdbusplus phosphor-logging" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/host/obmc-op-control-host%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/host/obmc-op-control-host%.bbappend new file mode 100644 index 000000000..5326680f6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/host/obmc-op-control-host%.bbappend @@ -0,0 +1,6 @@ +SYSTEMD_LINK_${PN}_remove += "../op-start-host@.service:obmc-host-startmin@0.target.requires/op-start-host@0.service" +SYSTEMD_LINK_${PN}_remove += "../op-init-pnor@.service:obmc-host-startmin@0.target.requires/op-init-pnor@0.service" + +FILES_${PN}_remove = "${systemd_unitdir}/system/obmc-host-startmin@0.target.requires" +FILES_${PN}_remove = "${systemd_unitdir}/system/obmc-host-startmin@0.target.requires/op-start-host@0.service" +FILES_${PN}_remove = "${systemd_unitdir}/system/obmc-host-startmin@0.target.requires/op-init-pnor@0.service"
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket new file mode 100644 index 000000000..8782e4dd3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket @@ -0,0 +1,9 @@ +[Unit] +Description=BMC Webserver socket + +[Socket] +ListenStream=443 +ReusePort=true + +[Install] +WantedBy=sockets.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend new file mode 100644 index 000000000..72d991c7e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend @@ -0,0 +1 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-channel-inventory%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-channel-inventory%.bbappend new file mode 100644 index 000000000..d79704ec6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-channel-inventory%.bbappend @@ -0,0 +1,4 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += " file://channel.yaml \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-channel-inventory%/channel.yaml b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-channel-inventory%/channel.yaml new file mode 100644 index 000000000..032e05127 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-channel-inventory%/channel.yaml @@ -0,0 +1,8 @@ +# Channel Number (must be unique) is the key +1: + # ifName the ethernet device name (used in the dbus path) + ifName: eth0 +2: + ifName: eth1 +3: + ifName: eth1 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config.bbappend new file mode 100644 index 000000000..616fb9a75 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config.bbappend @@ -0,0 +1,21 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +SRC_URI += " file://dev_id.json \ + file://channel_access.json \ + file://channel_config.json \ + file://master_write_read_white_list.json \ + " + +FILES_${PN} += " \ + ${datadir}/ipmi-providers/channel_access.json \ + ${datadir}/ipmi-providers/channel_config.json \ + ${datadir}/ipmi-providers/master_write_read_white_list.json \ + " + +do_install_append() { + install -m 0644 -D ${WORKDIR}/channel_access.json \ + ${D}${datadir}/ipmi-providers/channel_access.json + install -m 0644 -D ${WORKDIR}/channel_config.json \ + ${D}${datadir}/ipmi-providers/channel_config.json + install -m 0644 -D ${WORKDIR}/master_write_read_white_list.json \ + ${D}${datadir}/ipmi-providers/master_write_read_white_list.json +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_access.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_access.json new file mode 100644 index 000000000..299483121 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_access.json @@ -0,0 +1,23 @@ +{ + "1" : { + "access_mode" : "always_available", + "user_auth_disabled" : false, + "per_msg_auth_disabled" : false, + "alerting_disabled" : false, + "priv_limit" : "priv-admin" + }, + "2" : { + "access_mode" : "always_available", + "user_auth_disabled" : false, + "per_msg_auth_disabled" : false, + "alerting_disabled" : false, + "priv_limit" : "priv-admin" + }, + "3" : { + "access_mode" : "always_available", + "user_auth_disabled" : false, + "per_msg_auth_disabled" : false, + "alerting_disabled" : false, + "priv_limit" : "priv-admin" + } +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_config.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_config.json new file mode 100644 index 000000000..13b945fd0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/channel_config.json @@ -0,0 +1,178 @@ +{ + "0" : { + "name" : "IPMB", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "1" : { + "name" : "eth1", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "lan-802.3", + "protocol_type" : "ipmb-1.0", + "session_supported" : "multi-session", + "is_ipmi" : true + } + }, + "2" : { + "name" : "eth2", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "lan-802.3", + "protocol_type" : "ipmb-1.0", + "session_supported" : "multi-session", + "is_ipmi" : true + } + }, + "3" : { + "name" : "eth0", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "lan-802.3", + "protocol_type" : "ipmb-1.0", + "session_supported" : "multi-session", + "is_ipmi" : true + } + }, + "4" : { + "name" : "EMP", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "5" : { + "name" : "ICMB", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "6" : { + "name" : "SMLINK", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "7" : { + "name" : "SMM", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "8" : { + "name" : "INTRABMC", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "9" : { + "name" : "SIPMB", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "10" : { + "name" : "PCIE", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "11" : { + "name" : "RESERVED", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "12" : { + "name" : "INTERNAL", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "13" : { + "name" : "RESERVED", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "14" : { + "name" : "SELF", + "is_valid" : false, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + }, + "15" : { + "name" : "SMS", + "is_valid" : true, + "active_sessions" : 0, + "channel_info" : { + "medium_type" : "ipmb", + "protocol_type" : "ipmb-1.0", + "session_supported" : "session-less", + "is_ipmi" : true + } + } +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/dev_id.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/dev_id.json new file mode 100644 index 000000000..e561569d9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/dev_id.json @@ -0,0 +1,2 @@ +{"id": 35, "revision": 0, "addn_dev_support": 191, + "manuf_id": 343, "prod_id": 123, "aux": 0} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/master_write_read_white_list.json b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/master_write_read_white_list.json new file mode 100644 index 000000000..9fdb3c916 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-config/master_write_read_white_list.json @@ -0,0 +1,49 @@ +{ + "filters": [ + { + "busId": "0x01", + "slaveAddr": "0x4d", + "command": "0x00" + }, + { + "busId": "0x01", + "slaveAddr": "0x57", + "command": "0x00" + }, + { + "busId": "0x02", + "slaveAddr": "0x40", + "command": "0x00" + }, + { + "busId": "0x02", + "slaveAddr": "0x49", + "command": "0x00" + }, + { + "busId": "0x02", + "slaveAddr": "0x51", + "command": "0x00" + }, + { + "busId": "0x03", + "slaveAddr": "0x44", + "command": "0x00" + }, + { + "busId": "0x03", + "slaveAddr": "0x68", + "command": "0x00" + }, + { + "busId": "0x06", + "slaveAddr": "0x40", + "command": "0x00" + }, + { + "busId": "0x07", + "slaveAddr": "0x51", + "command": "0x00" + } + ] +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native.bbappend new file mode 100644 index 000000000..2d892ad1a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native.bbappend @@ -0,0 +1,8 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" +SRC_URI += " file://config.yaml" + +#override source file before it is used for final FRU file (merged from multiple sources) +do_install() { + cp ${WORKDIR}/config.yaml ${config_datadir}/ +} + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native/config.yaml b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native/config.yaml new file mode 100644 index 000000000..e9b7a621e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-fru-merge-config-native/config.yaml @@ -0,0 +1,31 @@ +# A YAML similar to this example would have to be generated, for eg with MRW +# inputs and system configuration, to depict IPMI Fru information. +# +# This file maps IPMI properties to phosphor dbus inventory properties +# +# This YAML could help generate C++ code. +# Format of the YAML: +# Fruid: +# Associated Fru paths +# d-bus Interfaces +# d-bus Properties +# IPMI Fru mapping +0: + /system/board/WFP_Baseboard: + entityID: 23 + entityInstance: 1 + interfaces: + xyz.openbmc_project.Inventory.Item: + name: + IPMIFruProperty: Product Name + IPMIFruSection: Product + xyz.openbmc_project.Inventory.Decorator.Asset: + Manufacturer: + IPMIFruProperty: Manufacturer + IPMIFruSection: Product + PartNumber: + IPMIFruProperty: Part Number + IPMIFruSection: Product + SerialNumber: + IPMIFruProperty: Serial Number + IPMIFruSection: Product diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0002-Modify-dbus-interface-for-power-control.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0002-Modify-dbus-interface-for-power-control.patch new file mode 100644 index 000000000..236bd18f4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0002-Modify-dbus-interface-for-power-control.patch @@ -0,0 +1,31 @@ +From 39df500f277eca01d6a0538d4db8ec34894d9441 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 17 Sep 2018 12:59:12 +0800 +Subject: [PATCH] Modify dbus interface for power control + +Switch power control service namespace from "org" to "xyz", +to compatible with new intel-chassis services + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + chassishandler.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index 6002e7a..0e83bba 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -786,8 +786,8 @@ ipmi_ret_t ipmi_get_chassis_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_data_len_t data_len, + ipmi_context_t context) + { +- const char* objname = "/org/openbmc/control/power0"; +- const char* intf = "org.openbmc.control.Power"; ++ const char* objname = "/xyz/openbmc_project/Chassis/Control/Power0"; ++ const char* intf = "xyz.openbmc_project.Chassis.Control.Power"; + + sd_bus* bus = NULL; + sd_bus_message* reply = NULL; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0003-Modify-dbus-interface-for-chassis-control.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0003-Modify-dbus-interface-for-chassis-control.patch new file mode 100644 index 000000000..9061481ac --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0003-Modify-dbus-interface-for-chassis-control.patch @@ -0,0 +1,33 @@ +From 48ac37551cd51415deafe8b1dcb23ebeef1e8ade Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 17 Sep 2018 13:04:42 +0800 +Subject: [PATCH] Modify-dbus-interface-for-chassis-control + +Switch chassis control service namespace from "org" to "xyz", +to compatible with new intel-chassis services + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + apphandler.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/apphandler.cpp b/apphandler.cpp +index b089331..f2889c5 100644 +--- a/apphandler.cpp ++++ b/apphandler.cpp +@@ -393,9 +393,9 @@ ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_data_len_t data_len, + ipmi_context_t context) + { +- const char* objname = "/org/openbmc/control/chassis0"; ++ const char* objname = "/xyz/openbmc_project/Chassis/Control/Chassis"; + const char* iface = "org.freedesktop.DBus.Properties"; +- const char* chassis_iface = "org.openbmc.control.Chassis"; ++ const char* chassis_iface = "xyz.openbmc_project.Chassis.Control.Chassis"; + sd_bus_message* reply = NULL; + sd_bus_error error = SD_BUS_ERROR_NULL; + int r = 0; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0009-IPv6-Network-changes.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0009-IPv6-Network-changes.patch new file mode 100644 index 000000000..3d9179ce5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0009-IPv6-Network-changes.patch @@ -0,0 +1,909 @@ +From cd4bc9e4291771f638f66efa205bf8fbec518546 Mon Sep 17 00:00:00 2001 +From: Vernon Mauery <vernon.mauery@linux.intel.com> +Date: Mon, 4 Feb 2019 10:30:12 -0800 +Subject: [PATCH] IPv6 Network changes + +Allow IPv6 IPMI set/get commands + +Signed-off-by: David Cobbley <david.j.cobbley@linux.intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> + +Change-Id: If5528d3b7294c5f8c17db5919439235d0fad0446 +--- + transporthandler.cpp | 667 ++++++++++++++++++++++++++++++++++++++++++- + transporthandler.hpp | 68 +++++ + types.hpp | 9 + + utils.hpp | 1 + + 4 files changed, 744 insertions(+), 1 deletion(-) + +Index: phosphor-host-ipmid.clean/transporthandler.cpp +=================================================================== +--- phosphor-host-ipmid.clean.orig/transporthandler.cpp ++++ phosphor-host-ipmid.clean/transporthandler.cpp +@@ -41,6 +41,12 @@ extern std::unique_ptr<phosphor::Timer> + + const int SIZE_MAC = 18; // xx:xx:xx:xx:xx:xx + constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4"; ++constexpr auto ipv6Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv6"; ++ ++static const std::array<std::string, 3> ipAddressEnablesType = { ++ "xyz.openbmc_project.Network.EthernetInterface.IPAllowed.IPv4Only", ++ "xyz.openbmc_project.Network.EthernetInterface.IPAllowed.IPv6Only", ++ "xyz.openbmc_project.Network.EthernetInterface.IPAllowed.IPv4AndIPv6"}; + + std::map<int, std::unique_ptr<struct ChannelConfig_t>> channelConfig; + +@@ -400,7 +406,6 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + ipmi_context_t context) + { + ipmi_ret_t rc = IPMI_CC_OK; +- *data_len = 0; + + using namespace std::chrono_literals; + +@@ -414,6 +419,9 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + auto reqptr = reinterpret_cast<const set_lan_t*>(request); + sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); + ++ size_t reqLen = *data_len; ++ *data_len = 0; ++ + // channel number is the lower nibble + int channel = reqptr->channel & CHANNEL_MASK; + auto ethdevice = ipmi::network::ChanneltoEthernet(channel); +@@ -437,6 +445,11 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + + case LanParam::IPSRC: + { ++ if (reqLen != LAN_PARAM_IPSRC_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ + uint8_t ipsrc{}; + std::memcpy(&ipsrc, reqptr->data, ipmi::network::IPSRC_SIZE_BYTE); + channelConf->ipsrc = static_cast<ipmi::network::IPOrigin>(ipsrc); +@@ -445,6 +458,11 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + + case LanParam::MAC: + { ++ if (reqLen != LAN_PARAM_MAC_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ + char mac[SIZE_MAC]; + + std::snprintf(mac, SIZE_MAC, ipmi::network::MAC_ADDRESS_FORMAT, +@@ -465,6 +483,11 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + + case LanParam::SUBNET: + { ++ if (reqLen != LAN_PARAM_SUBNET_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ + std::snprintf(netmask, INET_ADDRSTRLEN, + ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0], + reqptr->data[1], reqptr->data[2], reqptr->data[3]); +@@ -474,6 +497,11 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + + case LanParam::GATEWAY: + { ++ if (reqLen != LAN_PARAM_GATEWAY_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ + std::snprintf(gateway, INET_ADDRSTRLEN, + ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0], + reqptr->data[1], reqptr->data[2], reqptr->data[3]); +@@ -483,6 +511,11 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + + case LanParam::VLAN: + { ++ if (reqLen != LAN_PARAM_VLAN_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ + uint16_t vlan{}; + std::memcpy(&vlan, reqptr->data, ipmi::network::VLAN_SIZE_BYTE); + // We are not storing the enable bit +@@ -495,6 +528,11 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + + case LanParam::INPROGRESS: + { ++ if (reqLen != LAN_PARAM_INPROGRESS_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ + if (reqptr->data[0] == SET_COMPLETE) + { + channelConf->lan_set_in_progress = SET_COMPLETE; +@@ -523,6 +561,122 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + } + break; + ++ case LanParam::IPV6_AND_IPV4_ENABLES: ++ { ++ if (reqLen != LAN_PARAM_IPV6_AND_IPV4_ENABLES_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ channelConf->ipv6AddressingEnables = reqptr->data[0]; ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ADDRESSES: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ADDRESSES_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ channelConf->ipv6AddressSource = ++ reqptr->data[1] & 0x81; // Looking at bit 0 and bit 7 ++ char tmpIPV6[INET6_ADDRSTRLEN]; ++ inet_ntop(AF_INET6, &reqptr->data[2], tmpIPV6, INET6_ADDRSTRLEN); ++ channelConf->ipv6Addr.assign(tmpIPV6); ++ channelConf->ipv6Prefix = reqptr->data[19]; ++ break; ++ } ++ ++ case LanParam::IPV6_ROUTER_ADDRESS_CONF_CTRL: ++ { ++ if (reqLen != LAN_PARAM_IPV6_ROUTER_ADDRESS_CONF_CTRL_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ channelConf->ipv6RouterAddressConfigControl = reqptr->data[0]; ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ROUTER_1_IP_ADDR: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ROUTER_1_IP_ADDR_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ char tmpIPV6[INET6_ADDRSTRLEN]; ++ inet_ntop(AF_INET6, reinterpret_cast<const void*>(reqptr->data), ++ tmpIPV6, INET6_ADDRSTRLEN); ++ channelConf->ipv6GatewayAddr.assign(tmpIPV6); ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ROUTER_1_PREFIX_LEN: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ROUTER_1_PREFIX_LEN_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ channelConf->ipv6GatewayPrefixLength = reqptr->data[0]; ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ROUTER_1_PREFIX_VAL: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ROUTER_1_PREFIX_VAL_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ char tmpIPV6[INET6_ADDRSTRLEN]; ++ inet_ntop(AF_INET6, reinterpret_cast<const void*>(reqptr->data), ++ tmpIPV6, INET6_ADDRSTRLEN); ++ channelConf->ipv6GatewayPrefixValue.assign(tmpIPV6); ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ROUTER_2_IP_ADDR: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ROUTER_2_IP_ADDR_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ char tmpIPV6[INET6_ADDRSTRLEN]; ++ inet_ntop(AF_INET6, reinterpret_cast<const void*>(reqptr->data), ++ tmpIPV6, INET6_ADDRSTRLEN); ++ channelConf->ipv6BackupGatewayAddr.assign(tmpIPV6); ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ROUTER_2_PREFIX_LEN: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ROUTER_2_PREFIX_LEN_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ channelConf->ipv6BackupGatewayPrefixLength = reqptr->data[0]; ++ break; ++ } ++ ++ case LanParam::IPV6_STATIC_ROUTER_2_PREFIX_VAL: ++ { ++ if (reqLen != LAN_PARAM_IPV6_STATIC_ROUTER_2_PREFIX_VAL_SIZE) ++ { ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ char tmpIPV6[INET6_ADDRSTRLEN]; ++ inet_ntop(AF_INET6, reinterpret_cast<const void*>(reqptr->data), ++ tmpIPV6, INET6_ADDRSTRLEN); ++ channelConf->ipv6BackupGatewayPrefixValue.assign(tmpIPV6); ++ break; ++ } ++ + default: + { + rc = IPMI_CC_PARM_NOT_SUPPORTED; +@@ -549,6 +703,7 @@ ipmi_ret_t ipmi_transport_get_lan(ipmi_n + ipmi_ret_t rc = IPMI_CC_OK; + *data_len = 0; + const uint8_t current_revision = 0x11; // Current rev per IPMI Spec 2.0 ++ sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; + + get_lan_t* reqptr = (get_lan_t*)request; + // channel number is the lower nibble +@@ -687,6 +842,489 @@ ipmi_ret_t ipmi_transport_get_lan(ipmi_n + static_cast<uint8_t>(cipherList.size()); + break; + } ++ case LanParam::IPV6_AND_IPV4_SUPPORTED: ++ { ++ uint8_t addressSupport = ++ 0x1; // Allow both IPv4 & IPv6 simultaneously ++ std::array<uint8_t, 2> buf = {current_revision, addressSupport}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_AND_IPV4_ENABLES: ++ { ++ // If DHCP, check if you have an ipv6 and ipv4 address. If static ++ // return not supported ++ ++ // 00h check if conf DHCP == ipv4 or off ++ // 01h check if conf DHCP == ipv6 ++ // 02h check if DHCP == true ++ ++ auto ethIP = ethdevice + "/" + ipmi::network::IPV6_TYPE; ++ std::string networkInterfacePath; ++ uint8_t ipVAddressEnables = 0; ++ ++ if (channelConf->lan_set_in_progress == SET_COMPLETE) ++ { ++ try ++ { ++ ipmi::ObjectTree ancestorMap; ++ // if the system has an ip object,then ++ // get the IP object. ++ auto ipObject = ++ ipmi::getDbusObject(bus, ipmi::network::IP_INTERFACE, ++ ipmi::network::ROOT, ethIP); ++ // Get the parent interface of the IP object. ++ try ++ { ++ ipmi::InterfaceList interfaces; ++ interfaces.emplace_back( ++ ipmi::network::ETHERNET_INTERFACE); ++ ++ ancestorMap = ipmi::getAllAncestors( ++ bus, ipObject.first, std::move(interfaces)); ++ } ++ catch (InternalFailure& e) ++ { ++ // if unable to get the parent interface ++ // then commit the error and return. ++ log<level::ERR>( ++ "Unable to get the parent interface", ++ entry("PATH=%s", ipObject.first.c_str()), ++ entry("INTERFACE=%s", ++ ipmi::network::ETHERNET_INTERFACE)); ++ return IPMI_CC_UNSPECIFIED_ERROR; ++ } ++ // for an ip object there would be single parent ++ // interface. ++ networkInterfacePath = ancestorMap.begin()->first; ++ } ++ catch (InternalFailure& e) ++ { ++ // if there is no ip configured on the system,then ++ // get the network interface object. ++ auto networkInterfaceObject = ipmi::getDbusObject( ++ bus, ipmi::network::ETHERNET_INTERFACE, ++ ipmi::network::ROOT, ethdevice); ++ ++ networkInterfacePath = networkInterfaceObject.first; ++ } ++ ++ std::string ipEnables = ++ sdbusplus::message::variant_ns::get<std::string>( ++ ipmi::getDbusProperty(bus, ipmi::network::SERVICE, ++ networkInterfacePath, ++ ipmi::network::ETHERNET_INTERFACE, ++ "IPAddressEnables")); ++ ++ // check if on off ipv4 ipv6, etc. ++ bool found = false; ++ for (uint8_t ii = 0; ii < ipAddressEnablesType.size(); ii++) ++ { ++ if (ipEnables == ipAddressEnablesType[ii]) ++ { ++ ipVAddressEnables = ii; ++ found = true; ++ break; ++ } ++ } ++ if (!found) ++ { ++ return IPMI_CC_PARM_NOT_SUPPORTED; ++ } ++ } ++ else ++ { ++ ipVAddressEnables = channelConf->ipv6AddressingEnables; ++ } ++ ++ std::array<uint8_t, 2> buf = {current_revision, ipVAddressEnables}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATUS: ++ { ++ // Number of IPV6 addresses that are supported ++ constexpr std::array<uint8_t, 3> statusData = {1, 1, 3}; ++ ++ std::array<uint8_t, 4> buf = {current_revision, statusData[0], ++ statusData[1], statusData[2]}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ADDRESSES: ++ { ++ // Only return set selector 0 ++ uint8_t ipv6SetSelector = 0; ++ std::string ipaddress; ++ auto ethIP = ethdevice + "/" + ipmi::network::IPV6_TYPE; ++ uint8_t ipv6AddressSource = 0; ++ uint8_t prefixLength = 0; ++ uint8_t status = 0; ++ if (channelConf->lan_set_in_progress == SET_COMPLETE) ++ { ++ try ++ { ++ auto ipObjectInfo = ++ ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE, ++ ipmi::network::ROOT, ethIP); ++ ++ auto properties = ipmi::getAllDbusProperties( ++ bus, ipObjectInfo.second, ipObjectInfo.first, ++ ipmi::network::IP_INTERFACE); ++ ++ std::string origin = ++ sdbusplus::message::variant_ns::get<std::string>( ++ properties["Origin"]); ++ if (sdbusplus::message::variant_ns::get<std::string>( ++ properties["Origin"]) == ++ "xyz.openbmc_project.Network.IP.AddressOrigin.Static") ++ { ++ ipaddress = ++ sdbusplus::message::variant_ns::get<std::string>( ++ properties["Address"]); ++ ipv6AddressSource = 0x81; // Looking at bit 0 and bit 7 ++ prefixLength = ++ sdbusplus::message::variant_ns::get<uint8_t>( ++ properties["PrefixLength"]); ++ status = 0; ++ } ++ } ++ // ignore the exception, as it is a valid condition that ++ // the system is not configured with any IP. ++ catch (InternalFailure& e) ++ { ++ // nothing to do. ++ } ++ } ++ else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) ++ { ++ ipv6AddressSource = channelConf->ipv6AddressSource; ++ ipaddress = channelConf->ipv6Addr.c_str(); ++ prefixLength = channelConf->ipv6Prefix; ++ status = 1; ++ } ++ ++ std::array<uint8_t, ipmi::network::IPV6_ADDRESS_STATUS_SIZE> buf = { ++ current_revision, ipv6SetSelector, ipv6AddressSource}; ++ inet_pton(AF_INET6, ipaddress.c_str(), ++ reinterpret_cast<void*>(&buf[3])); ++ buf[20] = prefixLength; ++ buf[21] = status; ++ ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_DHCPV6_STATIC_DUID_STORAGE_LENGTH: ++ { ++ // DHCP unique identified ++ // Only 1 read-only 16-byte Block needed ++ uint8_t duidLength = 1; ++ std::array<uint8_t, 2> buf = {current_revision, duidLength}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_DHCPV6_STATIC_DUIDS: ++ { ++ std::string macAddress; ++ if (channelConf->lan_set_in_progress == SET_COMPLETE) ++ { ++ auto macObjectInfo = ++ ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE, ++ ipmi::network::ROOT, ethdevice); ++ ++ auto variant = ipmi::getDbusProperty( ++ bus, macObjectInfo.second, macObjectInfo.first, ++ ipmi::network::MAC_INTERFACE, "MACAddress"); ++ ++ macAddress = ++ sdbusplus::message::variant_ns::get<std::string>(variant); ++ } ++ else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) ++ { ++ macAddress = channelConf->macAddress; ++ } ++ ++ std::array<uint8_t, ++ ipmi::network::IPV6_DUID_SIZE + sizeof(current_revision)> ++ buf; ++ buf = {current_revision, ++ reqptr->parameter_set, ++ reqptr->parameter_block, ++ DUID_LEN, ++ 0, // Filler byte ++ DUID_LL_TYPE, ++ 0, // Filler byte ++ DUIC_ETH_HW_TYPE}; ++ sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT, ++ (&buf[8]), (&buf[9]), (&buf[10]), (&buf[11]), (&buf[12]), ++ (&buf[13])); ++ ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_DYNAMIC_ADDRESSES: ++ { ++ std::string ipaddress; ++ uint8_t ipv6AddressSource = 0; ++ uint8_t prefixLength = 0; ++ uint8_t status = 0; ++ auto ethIP = ethdevice + "/" + ipmi::network::IPV6_TYPE; ++ ++ if (channelConf->lan_set_in_progress == SET_COMPLETE) ++ { ++ try ++ { ++ auto ipObjectInfo = ++ ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE, ++ ipmi::network::ROOT, ethIP); ++ ++ auto properties = ipmi::getAllDbusProperties( ++ bus, ipObjectInfo.second, ipObjectInfo.first, ++ ipmi::network::IP_INTERFACE); ++ ++ if (sdbusplus::message::variant_ns::get<std::string>( ++ properties["Origin"]) == ++ "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP") ++ { ++ ipaddress = ++ sdbusplus::message::variant_ns::get<std::string>( ++ properties["Address"]); ++ ipv6AddressSource = 0x81; // Looking at bit 0 and bit 7 ++ prefixLength = ++ sdbusplus::message::variant_ns::get<uint8_t>( ++ properties["PrefixLength"]); ++ status = 0; ++ } ++ else ++ { ++ status = 1; ++ } ++ } ++ // ignore the exception, as it is a valid condition that ++ // the system is not configured with any IP. ++ catch (InternalFailure& e) ++ { ++ // nothing to do. ++ } ++ } ++ else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) ++ { ++ ipaddress = channelConf->ipv6Addr; ++ ipv6AddressSource = channelConf->ipv6AddressSource; ++ prefixLength = channelConf->ipv6Prefix; ++ status = channelConf->ipv6AddressStatus; ++ } ++ ++ uint8_t ipv6SetSelector = 0; ++ std::array<uint8_t, 22> buf = {current_revision, ipv6SetSelector, ++ ipv6AddressSource}; ++ inet_pton(AF_INET6, ipaddress.c_str(), ++ reinterpret_cast<void*>(&buf[3])); ++ buf[20] = prefixLength; ++ buf[21] = status; ++ ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_DHCPV6_DYNAMIC_DUID_STOR_LEN: ++ { ++ uint8_t duidLength = 0; ++ // Only 1 read-only 16-byte Block needed ++ duidLength = 1; ++ ++ std::array<uint8_t, 2> buf = {current_revision, duidLength}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_DHCPV6_DYNAMIC_DUIDS: ++ { ++ std::string macAddress; ++ if (channelConf->lan_set_in_progress == SET_COMPLETE) ++ { ++ auto macObjectInfo = ++ ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE, ++ ipmi::network::ROOT, ethdevice); ++ ++ auto variant = ipmi::getDbusProperty( ++ bus, macObjectInfo.second, macObjectInfo.first, ++ ipmi::network::MAC_INTERFACE, "MACAddress"); ++ ++ macAddress = ++ sdbusplus::message::variant_ns::get<std::string>(variant); ++ } ++ else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) ++ { ++ macAddress = channelConf->macAddress; ++ } ++ ++ std::array<uint8_t, ++ ipmi::network::IPV6_DUID_SIZE + sizeof(current_revision)> ++ buf; ++ buf = {current_revision, ++ reqptr->parameter_set, ++ reqptr->parameter_block, ++ DUID_LEN, ++ 0, // Filler byte ++ DUID_LL_TYPE, ++ 0, // Filler byte ++ DUIC_ETH_HW_TYPE}; ++ ++ sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT, ++ (&buf[8]), (&buf[9]), (&buf[10]), (&buf[11]), (&buf[12]), ++ (&buf[13])); ++ ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_ROUTER_ADDRESS_CONF_CTRL: ++ { ++ // Determine if automated router discovery occurs when static ++ // addresses are used for the bmc ++ ++ auto ethIP = ethdevice + "/" + ipmi::network::IPV6_TYPE; ++ std::string networkInterfacePath; ++ uint8_t dynamicRA; ++ if (channelConf->lan_set_in_progress == SET_COMPLETE) ++ { ++ ++ try ++ { ++ ipmi::ObjectTree ancestorMap; ++ // if the system is having ip object,then ++ // get the IP object. ++ auto ipObject = ++ ipmi::getDbusObject(bus, ipmi::network::IP_INTERFACE, ++ ipmi::network::ROOT, ethIP); ++ ++ // Get the parent interface of the IP object. ++ try ++ { ++ ipmi::InterfaceList interfaces; ++ interfaces.emplace_back( ++ ipmi::network::ETHERNET_INTERFACE); ++ ++ ancestorMap = ipmi::getAllAncestors( ++ bus, ipObject.first, std::move(interfaces)); ++ } ++ catch (InternalFailure& e) ++ { ++ // if unable to get the parent interface ++ // then commit the error and return. ++ log<level::ERR>( ++ "Unable to get the parent interface", ++ entry("PATH=%s", ipObject.first.c_str()), ++ entry("INTERFACE=%s", ++ ipmi::network::ETHERNET_INTERFACE)); ++ return IPMI_CC_UNSPECIFIED_ERROR; ++ } ++ // for an ip object there would be single parent ++ // interface. ++ networkInterfacePath = ancestorMap.begin()->first; ++ } ++ catch (InternalFailure& e) ++ { ++ // if there is no ip configured on the system,then ++ // get the network interface object. ++ auto networkInterfaceObject = ipmi::getDbusObject( ++ bus, ipmi::network::ETHERNET_INTERFACE, ++ ipmi::network::ROOT, ethdevice); ++ ++ networkInterfacePath = networkInterfaceObject.first; ++ } ++ ++ auto variant = ipmi::getDbusProperty( ++ bus, ipmi::network::SERVICE, networkInterfacePath, ++ ipmi::network::ETHERNET_INTERFACE, "IPv6AcceptRA"); ++ dynamicRA = sdbusplus::message::variant_ns::get<bool>(variant); ++ } ++ else ++ { ++ dynamicRA = channelConf->ipv6RouterAddressConfigControl; ++ } ++ ++ std::array<uint8_t, 2> buf = {current_revision, dynamicRA}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ROUTER_1_IP_ADDR: ++ { ++ std::array<uint8_t, ipmi::network::IPV6_ADDRESS_SIZE_BYTE + ++ sizeof(current_revision)> ++ buf = {current_revision}; ++ inet_pton(AF_INET6, channelConf->ipv6GatewayAddr.c_str(), ++ reinterpret_cast<void*>(&buf[1])); ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ROUTER_1_PREFIX_LEN: ++ { ++ std::array<uint8_t, 2> buf = {current_revision, ++ channelConf->ipv6GatewayPrefixLength}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ROUTER_1_PREFIX_VAL: ++ { ++ constexpr uint8_t setSelector = 0; ++ std::array<uint8_t, sizeof(setSelector) + ++ ipmi::network::IPV6_ADDRESS_SIZE_BYTE + ++ sizeof(current_revision)> ++ buf = {current_revision, setSelector}; ++ ++ inet_pton(AF_INET6, channelConf->ipv6GatewayPrefixValue.c_str(), ++ reinterpret_cast<void*>(&buf[2])); ++ ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ROUTER_2_IP_ADDR: ++ { ++ std::array<uint8_t, ipmi::network::IPV6_ADDRESS_SIZE_BYTE + ++ sizeof(current_revision)> ++ buf = {current_revision}; ++ inet_pton(AF_INET6, channelConf->ipv6BackupGatewayAddr.c_str(), ++ reinterpret_cast<void*>(&buf[1])); ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ROUTER_2_PREFIX_LEN: ++ { ++ std::array<uint8_t, 2> buf = { ++ current_revision, channelConf->ipv6BackupGatewayPrefixLength}; ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } ++ case LanParam::IPV6_STATIC_ROUTER_2_PREFIX_VAL: ++ { ++ ++ constexpr uint8_t setSelector = 0; ++ std::array<uint8_t, sizeof(setSelector) + ++ ipmi::network::IPV6_ADDRESS_SIZE_BYTE + ++ sizeof(current_revision)> ++ buf = {current_revision, setSelector}; ++ inet_pton(AF_INET6, ++ channelConf->ipv6BackupGatewayPrefixValue.c_str(), ++ reinterpret_cast<void*>(&buf[2])); ++ ++ std::copy(buf.begin(), buf.end(), static_cast<uint8_t*>(response)); ++ *data_len = buf.size(); ++ break; ++ } + default: + log<level::ERR>("Unsupported parameter", + entry("PARAMETER=0x%x", reqptr->parameter)); +@@ -932,6 +1570,16 @@ void applyChanges(int channel) + ipaddress, prefix); + } + ++ if (!channelConf->ipv6Addr.empty() && ++ channelConf->ipv6AddressSource == ++ 0x80) // Check if IPv6 static addresses are enabled ++ { ++ ipmi::network::createIP(bus, ipmi::network::SERVICE, ++ networkInterfacePath, ipv6Protocol, ++ channelConf->ipv6Addr, ++ channelConf->ipv6Prefix); ++ } ++ + if (!gateway.empty()) + { + ipmi::setDbusProperty(bus, systemObject.second, +@@ -939,7 +1587,24 @@ void applyChanges(int channel) + ipmi::network::SYSTEMCONFIG_INTERFACE, + "DefaultGateway", std::string(gateway)); + } ++ else if (!channelConf->ipv6GatewayAddr.empty()) ++ { ++ ipmi::setDbusProperty( ++ bus, systemObject.second, systemObject.first, ++ ipmi::network::SYSTEMCONFIG_INTERFACE, "DefaultGateway", ++ std::string(channelConf->ipv6GatewayAddr)); ++ } + } ++ // set IPAddress Enables ++ ipmi::setDbusProperty( ++ bus, ipmi::network::SERVICE, networkInterfaceObject.first, ++ ipmi::network::ETHERNET_INTERFACE, "IPAddressEnables", ++ ipAddressEnablesType[channelConf->ipv6AddressingEnables]); ++ ++ ipmi::setDbusProperty( ++ bus, ipmi::network::SERVICE, networkInterfaceObject.first, ++ ipmi::network::ETHERNET_INTERFACE, "IPv6AcceptRA", ++ (bool)channelConf->ipv6RouterAddressConfigControl); + } + catch (InternalFailure& e) + { +Index: phosphor-host-ipmid.clean/transporthandler.hpp +=================================================================== +--- phosphor-host-ipmid.clean.orig/transporthandler.hpp ++++ phosphor-host-ipmid.clean/transporthandler.hpp +@@ -80,6 +80,28 @@ enum class LanParam : uint8_t + IPV6_NEIGHBOR_TIMING_CONFIGURATION = 80, + }; + ++// Data length of parameters ++constexpr size_t LAN_PARAM_INPROGRESS_SIZE = 3; ++constexpr size_t LAN_PARAM_IP_SIZE = 6; ++constexpr size_t LAN_PARAM_IPSRC_SIZE = 3; ++constexpr size_t LAN_PARAM_MAC_SIZE = 8; ++constexpr size_t LAN_PARAM_SUBNET_SIZE = 6; ++constexpr size_t LAN_PARAM_GATEWAY_SIZE = 6; ++constexpr size_t LAN_PARAM_VLAN_SIZE = 4; ++constexpr size_t LAN_PARAM_IPV6_AND_IPV4_ENABLES_SIZE = 3; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ADDRESSES_SIZE = 23; ++constexpr size_t LAN_PARAM_IPV6_ROUTER_ADDRESS_CONF_CTRL_SIZE = 3; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ROUTER_1_IP_ADDR_SIZE = 18; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ROUTER_1_PREFIX_LEN_SIZE = 3; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ROUTER_1_PREFIX_VAL_SIZE = 19; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ROUTER_2_IP_ADDR_SIZE = 18; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ROUTER_2_PREFIX_LEN_SIZE = 3; ++constexpr size_t LAN_PARAM_IPV6_STATIC_ROUTER_2_PREFIX_VAL_SIZE = 19; ++ ++constexpr uint8_t DUID_LEN = 10; ++constexpr uint8_t DUID_LL_TYPE = 3; ++constexpr uint8_t DUIC_ETH_HW_TYPE = 1; ++ + constexpr uint8_t SET_COMPLETE = 0; + constexpr uint8_t SET_IN_PROGRESS = 1; + constexpr uint8_t SET_COMMIT_WRITE = 2; // Optional +@@ -102,6 +124,20 @@ struct ChannelConfig_t + uint8_t lan_set_in_progress = SET_COMPLETE; + bool flush = false; + ++ // IPV6 parameters ++ uint8_t ipv6AddressSource = 0x0; ++ uint8_t ipv6AddressingEnables = 0x2; ++ std::string ipv6Addr; ++ uint8_t ipv6Prefix = 32; ++ uint8_t ipv6AddressStatus = 0x0; ++ uint8_t ipv6RouterAddressConfigControl = 0x0; ++ std::string ipv6GatewayAddr; ++ std::string ipv6BackupGatewayAddr; ++ uint8_t ipv6GatewayPrefixLength; ++ std::string ipv6GatewayPrefixValue; ++ uint8_t ipv6BackupGatewayPrefixLength = 0x0; ++ std::string ipv6BackupGatewayPrefixValue; ++ + void clear() + { + ipaddr.clear(); +@@ -112,6 +148,20 @@ struct ChannelConfig_t + ipsrc = ipmi::network::IPOrigin::UNSPECIFIED; + lan_set_in_progress = SET_COMPLETE; + flush = false; ++ ++ // IPv6 ++ ipv6Addr.clear(); ++ ipv6GatewayAddr.clear(); ++ ipv6BackupGatewayAddr.clear(); ++ ipv6AddressingEnables = 0x2; ++ ipv6AddressSource = 0x0; ++ ipv6Prefix = 32; ++ ipv6AddressStatus = 0x0; ++ ipv6RouterAddressConfigControl = 0x0; ++ ipv6GatewayPrefixLength = 0x0; ++ ipv6GatewayPrefixValue.clear(); ++ ipv6BackupGatewayPrefixLength = 0x0; ++ ipv6BackupGatewayPrefixValue.clear(); + } + }; + +Index: phosphor-host-ipmid.clean/types.hpp +=================================================================== +--- phosphor-host-ipmid.clean.orig/types.hpp ++++ phosphor-host-ipmid.clean/types.hpp +@@ -209,6 +209,7 @@ constexpr auto ADDR_TYPE_FORMAT = "%hhx" + + constexpr auto IPV4_ADDRESS_SIZE_BYTE = 4; + constexpr auto IPV6_ADDRESS_SIZE_BYTE = 16; ++constexpr auto IPV6_ADDRESS_STATUS_SIZE = 22; + + constexpr auto DEFAULT_MAC_ADDRESS = "00:00:00:00:00:00"; + constexpr auto DEFAULT_ADDRESS = "0.0.0.0"; +@@ -220,6 +221,7 @@ constexpr auto BITS_32 = 32; + constexpr auto MASK_32_BIT = 0xFFFFFFFF; + constexpr auto VLAN_ID_MASK = 0x00000FFF; + constexpr auto VLAN_ENABLE_MASK = 0x8000; ++constexpr auto IPV6_DUID_SIZE = 18; + + enum class IPOrigin : uint8_t + { +@@ -228,5 +230,12 @@ enum class IPOrigin : uint8_t + DHCP = 2, + }; + ++enum class AddressingEnables : uint8_t ++{ ++ IPv4Only = 0, ++ IPv6Only = 1, ++ IPv4AndIPv6 = 2, ++}; ++ + } // namespace network + } // namespace ipmi +Index: phosphor-host-ipmid.clean/utils.hpp +=================================================================== +--- phosphor-host-ipmid.clean.orig/utils.hpp ++++ phosphor-host-ipmid.clean/utils.hpp +@@ -246,6 +246,7 @@ namespace network + constexpr auto ROOT = "/xyz/openbmc_project/network"; + constexpr auto SERVICE = "xyz.openbmc_project.Network"; + constexpr auto IP_TYPE = "ipv4"; ++constexpr auto IPV6_TYPE = "ipv6"; + constexpr auto IPV4_PREFIX = "169.254"; + constexpr auto IPV6_PREFIX = "fe80"; + constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP"; diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0010-fix-get-system-GUID-ipmi-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0010-fix-get-system-GUID-ipmi-command.patch new file mode 100644 index 000000000..c1ec6ac6e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0010-fix-get-system-GUID-ipmi-command.patch @@ -0,0 +1,56 @@ +From 4953a9f2233fd24a28da84443cea6aebecd14fbc Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 17 Sep 2018 13:20:54 +0800 +Subject: [PATCH] fix "get system GUID" ipmi command + +Change-Id: I15c71607c24ad8b3e2c9065a5470002ecb1761bb +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + apphandler.cpp | 7 ++----- + host-ipmid-whitelist.conf | 1 + + 2 files changed, 3 insertions(+), 5 deletions(-) + +diff --git a/apphandler.cpp b/apphandler.cpp +index f2889c5..9149373 100644 +--- a/apphandler.cpp ++++ b/apphandler.cpp +@@ -48,7 +48,7 @@ extern sd_bus* bus; + + constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC"; + constexpr auto bmc_state_property = "CurrentBMCState"; +-constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc"; ++// phosphor-setting-manager is the unique service that holds this interface + constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID"; + constexpr auto bmc_guid_property = "UUID"; + constexpr auto bmc_guid_len = 16; +@@ -546,8 +545,7 @@ ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + { + // Get the Inventory object implementing BMC interface + ipmi::DbusObjectInfo bmcObject = +- ipmi::getDbusObject(bus, bmc_interface); +- ++ ipmi::getDbusObject(bus, bmc_guid_interface); + // Read UUID property value from bmcObject + // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 + auto variant = +@@ -591,7 +589,6 @@ ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + catch (const InternalFailure& e) + { + log<level::ERR>("Failed in reading BMC UUID property", +- entry("INTERFACE=%s", bmc_interface), + entry("PROPERTY_INTERFACE=%s", bmc_guid_interface), + entry("PROPERTY=%s", bmc_guid_property)); + return IPMI_CC_UNSPECIFIED_ERROR; +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index 2c37ac9..164edbe 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -40,3 +40,4 @@ + 0x2C:0x06 //<Group Extension>:<Get Asset Tag> + 0x2C:0x07 //<Group Extension>:<Get Sensor Info> + 0x2C:0x10 //<Group Extension>:<Get Temperature Readings> ++0x30:0x41 //<OEM>:<Set System GUID> +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0012-ipmi-set-get-boot-options.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0012-ipmi-set-get-boot-options.patch new file mode 100644 index 000000000..243015c95 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0012-ipmi-set-get-boot-options.patch @@ -0,0 +1,64 @@ +From 7b5c6a54c049a447b1fd3a42f9d63322dcee4dc7 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Sun, 16 Sep 2018 19:45:10 +0800 +Subject: [PATCH] [ipmi] set/get boot options + +1. fix issue for handling unsupported paramter +2. add support for floppy/USB boot + +Change-Id: I2b888c1ad67fec7924dd5825f78622cd216a55f4 +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + chassishandler.cpp | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index 666addb..77af2dc 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -1244,7 +1244,8 @@ constexpr auto ipmiDefault = 0; + std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = { + {0x01, Source::Sources::Network}, + {0x02, Source::Sources::Disk}, +- {0x05, Source::Sources::ExternalMedia}, ++ {0x05, Source::Sources::DVD}, ++ {0x0f, Source::Sources::Removable}, + {ipmiDefault, Source::Sources::Default}}; + + std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = { +@@ -1255,7 +1256,8 @@ std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = { + std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = { + {Source::Sources::Network, 0x01}, + {Source::Sources::Disk, 0x02}, +- {Source::Sources::ExternalMedia, 0x05}, ++ {Source::Sources::DVD, 0x05}, ++ {Source::Sources::Removable, 0x0f}, + {Source::Sources::Default, ipmiDefault}}; + + std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = { +@@ -1533,7 +1535,7 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + setBootMode(Mode::Modes::Regular); + } + } +- if (modeIpmiToDbus.end() != modeItr) ++ else if (modeIpmiToDbus.end() != modeItr) + { + rc = setBootMode(modeItr->second); + if (rc != IPMI_CC_OK) +@@ -1550,6 +1552,12 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + setBootSource(Source::Sources::Default); + } + } ++ else ++ { ++ // if boot option is not in support list, return error ++ *data_len = 0; ++ return IPMI_CC_INVALID_FIELD_REQUEST; ++ } + } + catch (InternalFailure& e) + { +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0013-ipmi-add-set-bios-id-to-whitelist.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0013-ipmi-add-set-bios-id-to-whitelist.patch new file mode 100644 index 000000000..ae10ab60a --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0013-ipmi-add-set-bios-id-to-whitelist.patch @@ -0,0 +1,25 @@ +From ad7276f3aedb6f5aed315db57406c98f2bf71a09 Mon Sep 17 00:00:00 2001 +From: "Jia, Chunhui" <chunhui.jia@intel.com> +Date: Tue, 24 Jul 2018 13:21:52 +0800 +Subject: [PATCH] [ipmi] add set bios id to whitelist + +Add "SetBIOSId" and "GetDeviceInfo" 2 OEM commands into whitelist + +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +--- + host-ipmid-whitelist.conf | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index 164edbe..db54a49 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -41,3 +41,5 @@ + 0x2C:0x07 //<Group Extension>:<Get Sensor Info> + 0x2C:0x10 //<Group Extension>:<Get Temperature Readings> + 0x30:0x41 //<OEM>:<Set System GUID> ++0x30:0x26 //<OEM>:<Set BIOS ID> ++0x30:0x27 //<OEM>:<Get Device Info> +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0014-Enable-get-device-guid-ipmi-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0014-Enable-get-device-guid-ipmi-command.patch new file mode 100644 index 000000000..46dd99466 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0014-Enable-get-device-guid-ipmi-command.patch @@ -0,0 +1,45 @@ +From 482a6cc52d0ec514d6da5f4bcb04b4991f3cc36e Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Mon, 17 Sep 2018 13:41:25 +0800 +Subject: [PATCH] Enable get device guid ipmi command + +The UUID interface is changed, modify the API to get the correct UUID +for device guid + +Change-Id: I0c0c7bd350992ac03f928707986a7180407d8f3f +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + apphandler.cpp | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/apphandler.cpp b/apphandler.cpp +index 937be71..89d797a 100644 +--- a/apphandler.cpp ++++ b/apphandler.cpp +@@ -392,9 +392,10 @@ ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_data_len_t data_len, + ipmi_context_t context) + { +- const char* objname = "/xyz/openbmc_project/Chassis/Control/Chassis"; ++ const char* objname = ++ "/xyz/openbmc_project/inventory/system/chassis/motherboard/bmc"; + const char* iface = "org.freedesktop.DBus.Properties"; +- const char* chassis_iface = "xyz.openbmc_project.Chassis.Control.Chassis"; ++ const char* uuid_iface = "xyz.openbmc_project.Common.UUID"; + sd_bus_message* reply = NULL; + sd_bus_error error = SD_BUS_ERROR_NULL; + int r = 0; +@@ -426,8 +427,9 @@ ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + entry("ERRNO=0x%X", -r)); + goto finish; + } ++ + r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply, +- "ss", chassis_iface, "uuid"); ++ "ss", uuid_iface, "UUID"); + if (r < 0) + { + log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r)); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0016-add-better-sdbusplus-exception-handling.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0016-add-better-sdbusplus-exception-handling.patch new file mode 100644 index 000000000..873eb6b16 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0016-add-better-sdbusplus-exception-handling.patch @@ -0,0 +1,153 @@ +From a445f287d4aebca68dc0321e292933311caf59ba Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Sun, 16 Sep 2018 20:14:55 +0800 +Subject: [PATCH] add better sdbusplus exception handling + +Now that sdbusplus throws, we need to catch more stuff. To compound the +problem, even though sdbusplus::exception::exception inherits from +std::exception, there is a problem that prevents the code from simply +catching std::exception. + +Change-Id: I2a330e542f5d87722a4c04e6d47de2cfb2f7d7c9 +Signed-off-by: Vernon Mauery <vernon.mauery@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> + +--- + apphandler.cpp | 14 +++++++-- + ipmid.cpp | 77 +++++++++++++++++++++++++++++++++++--------------- + 2 files changed, 66 insertions(+), 25 deletions(-) + +diff --git a/apphandler.cpp b/apphandler.cpp +index 126de33..3cae6d5 100644 +--- a/apphandler.cpp ++++ b/apphandler.cpp +@@ -312,9 +312,19 @@ ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + auto version = getActiveSoftwareVersionInfo(); + r = convert_version(version.c_str(), &rev); + } +- catch (const std::exception& e) ++ catch (sdbusplus::exception::exception& e) + { +- log<level::ERR>(e.what()); ++ log<level::ERR>("sdbusplus::exception", ++ entry("ERROR=%s", e.what())); ++ } ++ catch (std::exception& e) ++ { ++ log<level::ERR>("unexpected exception", ++ entry("ERROR=%s", e.what())); ++ } ++ catch (...) ++ { ++ log<level::ERR>("unknown exception"); + } + + if (r >= 0) +diff --git a/ipmid.cpp b/ipmid.cpp +index 2d48bfe..8d2fb37 100644 +--- a/ipmid.cpp ++++ b/ipmid.cpp +@@ -273,6 +273,10 @@ ipmi_ret_t ipmi_netfn_router(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + } + // IPMI command handlers can throw unhandled exceptions, catch those + // and return sane error code. ++ catch (sdbusplus::exception::exception& e) ++ { ++ log<level::ERR>("sdbusplus exception", entry("EXCEPTION=%s", e.what())); ++ } + catch (const std::exception& e) + { + log<level::ERR>(e.what(), entry("NET_FUN=0x%X", netfn), +@@ -281,6 +285,23 @@ ipmi_ret_t ipmi_netfn_router(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + *data_len = 0; + // fall through + } ++ catch (...) ++ { ++ std::exception_ptr eptr = std::current_exception(); ++ try ++ { ++ std::rethrow_exception(eptr); ++ } ++ catch (std::exception& e) ++ { ++ log<level::ERR>("unexpected uncaught exception", ++ entry("EXCEPTION=%s", e.what()), ++ entry("NET_FUN=0x%X", netfn), ++ entry("CMD=0x%X", cmd)); ++ rc = IPMI_CC_UNSPECIFIED_ERROR; ++ *data_len = 0; ++ } ++ } + // Now copy the return code that we got from handler and pack it in first + // byte. + std::memcpy(response, &rc, IPMI_CC_LEN); +@@ -361,32 +382,42 @@ final: + void cache_restricted_mode() + { + restricted_mode = false; +- using namespace sdbusplus::xyz::openbmc_project::Control::Security::server; +- using namespace internal; +- using namespace internal::cache; +- sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); +- const auto& restrictionModeSetting = +- objects->map.at(restrictionModeIntf).front(); +- auto method = dbus.new_method_call( +- objects->service(restrictionModeSetting, restrictionModeIntf).c_str(), +- restrictionModeSetting.c_str(), "org.freedesktop.DBus.Properties", +- "Get"); +- method.append(restrictionModeIntf, "RestrictionMode"); +- auto resp = dbus.call(method); +- if (resp.is_method_error()) ++ try + { +- log<level::ERR>("Error in RestrictionMode Get"); +- // Fail-safe to true. +- restricted_mode = true; +- return; ++ using namespace sdbusplus::xyz::openbmc_project::Control::Security:: ++ server; ++ using namespace internal; ++ using namespace internal::cache; ++ sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); ++ const auto& restrictionModeSetting = ++ objects->map.at(restrictionModeIntf).front(); ++ auto method = dbus.new_method_call( ++ objects->service(restrictionModeSetting, restrictionModeIntf) ++ .c_str(), ++ restrictionModeSetting.c_str(), "org.freedesktop.DBus.Properties", ++ "Get"); ++ method.append(restrictionModeIntf, "RestrictionMode"); ++ auto resp = dbus.call(method); ++ if (resp.is_method_error()) ++ { ++ log<level::ERR>("Error in RestrictionMode Get"); ++ // Fail-safe to true. ++ restricted_mode = true; ++ return; ++ } ++ sdbusplus::message::variant<std::string> result; ++ resp.read(result); ++ auto restrictionMode = RestrictionMode::convertModesFromString( ++ sdbusplus::message::variant_ns::get<std::string>(result)); ++ if (RestrictionMode::Modes::Whitelist == restrictionMode) ++ { ++ restricted_mode = true; ++ } + } +- sdbusplus::message::variant<std::string> result; +- resp.read(result); +- auto restrictionMode = RestrictionMode::convertModesFromString( +- variant_ns::get<std::string>(result)); +- if (RestrictionMode::Modes::Whitelist == restrictionMode) ++ catch (sdbusplus::exception::exception& e) + { +- restricted_mode = true; ++ // restrictionModeIntf does not exist; default to not enforcing ++ log<level::ERR>("sdbusplus exception", entry("EXCEPTION=%s", e.what())); + } + } + +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0018-Catch-sdbusplus-exceptions-in-IPMI-net.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0018-Catch-sdbusplus-exceptions-in-IPMI-net.patch new file mode 100644 index 000000000..6fa69b602 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0018-Catch-sdbusplus-exceptions-in-IPMI-net.patch @@ -0,0 +1,49 @@ +From 4490ee7a9fd054640af7a9da3400f76195dc2880 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Sun, 16 Sep 2018 21:03:58 +0800 +Subject: [PATCH] Catch sdbusplus exceptions in IPMI net + +Missing the correct exception was causing issues with setting the IPV4 +address + +Change-Id: Ieaaacfcbaec82a0c3b110889817a7ceb9cda8d3c +Signed-off-by: Dave Cobbley <david.j.cobbley@linux.intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + transporthandler.cpp | 2 +- + utils.cpp | 5 +++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/transporthandler.cpp b/transporthandler.cpp +index 6f4ec3f..6cb3feb 100644 +--- a/transporthandler.cpp ++++ b/transporthandler.cpp +@@ -1559,7 +1559,7 @@ void applyChanges(int channel) + ipmi::network::ETHERNET_INTERFACE, "IPv6AcceptRA", + (bool)channelConf->ipv6RouterAddressConfigControl); + } +- catch (InternalFailure& e) ++ catch (sdbusplus::exception::exception& e) + { + log<level::ERR>( + "Failed to set network data", entry("PREFIX=%d", prefix), +diff --git a/utils.cpp b/utils.cpp +index 225b1cc..d10b5de 100644 +--- a/utils.cpp ++++ b/utils.cpp +@@ -358,9 +358,10 @@ void deleteAllDbusObjects(sdbusplus::bus::bus& bus, + "Delete"); + } + } +- catch (InternalFailure& e) ++ catch (sdbusplus::exception::exception& e) + { +- log<level::INFO>("Unable to delete the objects having", ++ log<level::INFO>("sdbusplus exception - Unable to delete the objects", ++ entry("ERROR=%s", e.what()), + entry("INTERFACE=%s", interface.c_str()), + entry("SERVICE=%s", serviceRoot.c_str())); + } +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0021-Implement-IPMI-Commmand-Get-Host-Restart-Cause.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0021-Implement-IPMI-Commmand-Get-Host-Restart-Cause.patch new file mode 100644 index 000000000..af526c177 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0021-Implement-IPMI-Commmand-Get-Host-Restart-Cause.patch @@ -0,0 +1,143 @@ +From c14e31ebc35e0bb7b843d84683f9f2698c9c08d7 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Sun, 16 Sep 2018 21:32:38 +0800 +Subject: [PATCH] Implement IPMI Commmand - Get Host Restart Cause. + +It supports to track the information about what +action last caused the system to restart. +Return value includes: restart_cause and channel_number. + +According to IPMI Spec, it includes 12 types as following: +1. Unknown 0x0 +2. IpmiCommand 0x1 +3. ResetButton 0x2 +4. PowerButton 0x3 +5. WatchdogTimer 0x4 +6. OEM 0x5 +7. PowerPolicyAlwaysOn 0x6 +8. PowerPolicyPreviousState 0x7 +9. PEF-Reset 0x8 +10. PEF-PowerCycle 0x9 +11. SoftReset 0xA +12. RTC-Wakeup 0xB + +Change-Id: Id3b32e271b85b5fc4c69d5ca40227f8f9c08ce48 +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + chassishandler.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++ + chassishandler.hpp | 1 + + host-ipmid-whitelist.conf | 1 + + 3 files changed, 56 insertions(+) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index 77af2dc..2a29755 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -107,6 +107,11 @@ static constexpr auto chassisPOHStateIntf = + "xyz.openbmc_project.State.PowerOnHours"; + static constexpr auto pOHCounterProperty = "POHCounter"; + static constexpr auto match = "chassis0"; ++const static constexpr char* stateHostInterface = ++ "xyz.openbmc_project.State.Host"; ++const static constexpr char* hostRestartCauseInterface = ++ "xyz.openbmc_project.State.Host.HostRestartCause"; ++const static constexpr char* hostRestartCause = "HostRestartCause"; + const static constexpr char chassisCapIntf[] = + "xyz.openbmc_project.Control.ChassisCapabilities"; + const static constexpr char chassisCapFlagsProp[] = "CapabilitiesFlags"; +@@ -324,6 +329,13 @@ struct set_sys_boot_options_t + uint8_t data[SIZE_BOOT_OPTION]; + } __attribute__((packed)); + ++struct GetSysRestartCauseResponse ++{ ++ uint8_t restartCause; ++ uint8_t channelNum; ++ ++} __attribute__((packed)); ++ + int getHostNetworkData(get_sys_boot_options_response_t* respptr) + { + ipmi::PropertyMap properties; +@@ -1598,6 +1610,44 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + return rc; + } + ++ipmi_ret_t ipmi_chassis_get_sys_restart_cause( ++ ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ++ ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) ++{ ++ ipmi_ret_t rc = IPMI_CC_OK; ++ ++ GetSysRestartCauseResponse* resp = (GetSysRestartCauseResponse*)response; ++ std::fill(reinterpret_cast<uint8_t*>(resp), ++ reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0); ++ if (*data_len != 0) ++ { ++ rc = IPMI_CC_REQ_DATA_LEN_INVALID; ++ return rc; ++ } ++ ++ try ++ { ++ sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; ++ ipmi::DbusObjectInfo hostObject = ++ ipmi::getDbusObject(bus, stateHostInterface); ++ ipmi::Value variant = ++ ipmi::getDbusProperty(bus, hostObject.second, hostObject.first, ++ hostRestartCauseInterface, hostRestartCause); ++ resp->restartCause = variant.get<uint8_t>(); ++ } ++ ++ catch (std::exception& e) ++ { ++ log<level::ERR>(e.what()); ++ rc = IPMI_CC_UNSPECIFIED_ERROR; ++ return rc; ++ } ++ resp->channelNum = 0; // Fix to primary channel. ++ *data_len = sizeof(GetSysRestartCauseResponse); ++ ++ return rc; ++} ++ + ipmi_ret_t ipmiGetPOHCounter(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_request_t request, ipmi_response_t response, + ipmi_data_len_t data_len, ipmi_context_t context) +@@ -1739,4 +1789,8 @@ void register_netfn_chassis_functions() + ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_RESTORE_POLICY, NULL, + ipmi_chassis_set_power_restore_policy, + PRIVILEGE_OPERATOR); ++ ++ // <get Host Restart Cause> ++ ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_RESTART_CAUSE, NULL, ++ ipmi_chassis_get_sys_restart_cause, PRIVILEGE_USER); + } +diff --git a/chassishandler.hpp b/chassishandler.hpp +index 0c6d5a2..e37c4f1 100644 +--- a/chassishandler.hpp ++++ b/chassishandler.hpp +@@ -17,6 +17,7 @@ enum ipmi_netfn_chassis_cmds + // Set Power Restore Policy + IPMI_CMD_SET_RESTORE_POLICY = 0x06, + // Get capability bits ++ IPMI_CMD_GET_SYS_RESTART_CAUSE = 0x07, + IPMI_CMD_SET_SYS_BOOT_OPTIONS = 0x08, + IPMI_CMD_GET_SYS_BOOT_OPTIONS = 0x09, + IPMI_CMD_GET_POH_COUNTER = 0x0F, +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index db54a49..827e2dc 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -3,6 +3,7 @@ + 0x00:0x02 //<Chassis>:<Chassis Control> + 0x00:0x05 //<Chassis>:<Set Chassis Capabilities> + 0x00:0x06 //<Chassis>:<Set Power Restore Policy> ++0x00:0x07 //<Chassis>:<Get System Restart Cause> + 0x00:0x08 //<Chassis>:<Set System Boot Options> + 0x00:0x09 //<Chassis>:<Get System Boot Options> + 0x00:0x0F //<Chassis>:<Get POH Counter Command> +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0039-ipmi-add-oem-command-get-AIC-FRU-to-whitelist.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0039-ipmi-add-oem-command-get-AIC-FRU-to-whitelist.patch new file mode 100644 index 000000000..fdaa91085 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0039-ipmi-add-oem-command-get-AIC-FRU-to-whitelist.patch @@ -0,0 +1,25 @@ +From cf466ba2c66a95825ae0014d7c378ad63b050d2f Mon Sep 17 00:00:00 2001 +From: "Jia, Chunhui" <chunhui.jia@intel.com> +Date: Wed, 15 Aug 2018 14:50:04 +0800 +Subject: [PATCH] [ipmi] add oem command "get AIC FRU" to whitelist + +Intel BIOS requires this oem command to get addon card FRU info. +Add to whitelist to unblock. + +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +--- + host-ipmid-whitelist.conf | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index db54a49..49746a2 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -43,3 +43,4 @@ + 0x30:0x41 //<OEM>:<Set System GUID> + 0x30:0x26 //<OEM>:<Set BIOS ID> + 0x30:0x27 //<OEM>:<Get Device Info> ++0x30:0x31 //<OEM>:<Get AIC card FRU> +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0048-Implement-IPMI-Master-Write-Read-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0048-Implement-IPMI-Master-Write-Read-command.patch new file mode 100644 index 000000000..4018dbffe --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0048-Implement-IPMI-Master-Write-Read-command.patch @@ -0,0 +1,322 @@ +From cd25f43461b41b74d19cd1f93ce301df9c3bd4f2 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Fri, 21 Sep 2018 09:21:14 +0800 +Subject: [PATCH] Implement IPMI Master Write-Read command + +This command can be used for low-level I2C/SMBus write, read, or write-read +accesses to the IPMB or private busses behind a management controller. + +The command can also be used for providing low-level access to devices +that provide an SMBus slave interface. + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + apphandler.cpp | 236 ++++++++++++++++++++++++++++++++++++++++++++++ + apphandler.hpp | 1 + + host-ipmid-whitelist.conf | 1 + + 3 files changed, 238 insertions(+) + +diff --git a/apphandler.cpp b/apphandler.cpp +index 17aff2a..2fe79f6 100644 +--- a/apphandler.cpp ++++ b/apphandler.cpp +@@ -8,6 +8,14 @@ + #include "types.hpp" + #include "utils.hpp" + ++#include <fcntl.h> ++#include <linux/i2c-dev.h> ++#include <linux/i2c.h> ++#include <sys/ioctl.h> ++#include <sys/stat.h> ++#include <sys/types.h> ++#include <unistd.h> ++ + #include <arpa/inet.h> + #include <host-ipmid/ipmid-api.h> + #include <limits.h> +@@ -55,6 +63,8 @@ constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID"; + constexpr auto bmc_guid_property = "UUID"; + constexpr auto bmc_guid_len = 16; + ++static constexpr uint8_t maxIPMIWriteReadSize = 144; ++ + static constexpr auto redundancyIntf = + "xyz.openbmc_project.Software.RedundancyPriority"; + static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version"; +@@ -86,6 +96,34 @@ typedef struct + uint8_t aux[4]; + } __attribute__((packed)) ipmi_device_id_t; + ++typedef struct ++{ ++ uint8_t busId; ++ uint8_t slaveAddr; ++ uint8_t readCount; ++} __attribute__((packed)) ipmiI2cRwReq; ++ ++typedef struct ++{ ++ uint8_t busId; ++ uint8_t slaveAddr; ++ std::vector<uint8_t> data; ++} ipmiMasterRwWhitelist; ++ ++static std::vector<ipmiMasterRwWhitelist>& getWhiteList() ++{ ++ static std::vector<ipmiMasterRwWhitelist> rwWhiteList; ++ return rwWhiteList; ++} ++ ++static constexpr const char* whiteListFilename = ++ "/usr/share/ipmi-providers/master_write_read_white_list.json"; ++ ++static constexpr const char* filtersStr = "filters"; ++static constexpr const char* busIdStr = "busId"; ++static constexpr const char* slaveAddrStr = "slaveAddr"; ++static constexpr const char* cmdStr = "command"; ++ + /** + * @brief Returns the Version info from primary s/w object + * +@@ -1089,8 +1127,195 @@ writeResponse: + return IPMI_CC_OK; + } + ++static int loadI2CWhiteList() ++{ ++ nlohmann::json data = nullptr; ++ std::ifstream jsonFile(whiteListFilename); ++ ++ if (!jsonFile.good()) ++ { ++ log<level::WARNING>("whitelist file not found!"); ++ return -1; ++ } ++ ++ try ++ { ++ data = nlohmann::json::parse(jsonFile, nullptr, false); ++ } ++ catch (nlohmann::json::parse_error& e) ++ { ++ log<level::ERR>("Corrupted whitelist config file", ++ entry("MSG: %s", e.what())); ++ return -1; ++ } ++ ++ try ++ { ++ unsigned int i = 0; ++ nlohmann::json filters = data[filtersStr].get<nlohmann::json>(); ++ getWhiteList().resize(filters.size()); ++ ++ for (const auto& it : filters.items()) ++ { ++ nlohmann::json filter = it.value(); ++ if (filter.is_null()) ++ { ++ log<level::ERR>("Incorrect filter"); ++ return -1; ++ } ++ ++ getWhiteList()[i].busId = ++ std::stoul(filter[busIdStr].get<std::string>(), nullptr, 16); ++ ++ getWhiteList()[i].slaveAddr = std::stoul( ++ filter[slaveAddrStr].get<std::string>(), nullptr, 16); ++ ++ std::string command = filter[cmdStr].get<std::string>(); ++ ++ log<level::DEBUG>("IPMI I2C whitelist ", entry("INDEX=%d", i), ++ entry("BUS=%d", getWhiteList()[i].busId), ++ entry("ADDR=0x%x", getWhiteList()[i].slaveAddr), ++ entry("LEN=0x%x", command.length()), ++ entry("COMMAND=[%s]", command.c_str())); ++ ++ // convert data string ++ std::istringstream iss(command); ++ std::string token; ++ while (std::getline(iss, token, ' ')) ++ { ++ log<level::DEBUG>("IPMI I2C command\n", ++ entry("TOKEN=%s", token.c_str())); ++ getWhiteList()[i].data.emplace_back( ++ std::stoul(token, nullptr, 16)); ++ } ++ i++; ++ } ++ } ++ catch (std::exception& e) ++ { ++ log<level::ERR>("unexpected exception", entry("ERROR=%s", e.what())); ++ return -1; ++ } ++ return 0; ++} ++ ++ipmi_ret_t ipmiMasterWriteRead(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ++ ipmi_request_t request, ipmi_response_t response, ++ ipmi_data_len_t data_len, ipmi_context_t context) ++{ ++ bool foundInList = false; ++ int ret = 0; ++ i2c_rdwr_ioctl_data msgRdwr = {0}; ++ i2c_msg i2cmsg[2] = {0}; ++ ipmiI2cRwReq* reqi2c = reinterpret_cast<ipmiI2cRwReq*>(request); ++ ++ if (*data_len <= sizeof(ipmiI2cRwReq)) ++ { ++ log<level::ERR>("Failed in request", entry("LEN=%d", *data_len)); ++ *data_len = 0; ++ return IPMI_CC_REQ_DATA_LEN_INVALID; ++ } ++ ++ if (reqi2c->readCount > maxIPMIWriteReadSize) ++ { ++ log<level::ERR>("Failed in request", entry("R=%d", reqi2c->readCount)); ++ *data_len = 0; ++ return IPMI_CC_PARM_OUT_OF_RANGE; ++ } ++ ++ uint8_t* resptr = reinterpret_cast<uint8_t*>(response); ++ uint8_t busId = (reqi2c->busId & 0xFF) >> 1; ++ // Convert the I2C address from 7-bit format ++ uint8_t i2cAddr = reqi2c->slaveAddr >> 1; ++ size_t writeCount = *data_len - sizeof(ipmiI2cRwReq); ++ ++ log<level::DEBUG>( ++ "INPUT: ", entry("LEN=%d", *data_len), entry("ID=0x%x", busId), ++ entry("ADDR=0x%x", reqi2c->slaveAddr), entry("R=%d", reqi2c->readCount), ++ entry("W=%d", writeCount)); ++ ++ *data_len = 0; ++ ++ std::vector<uint8_t> inBuf(reqi2c->readCount); ++ std::vector<uint8_t> outBuf(writeCount); ++ uint8_t* reqptr = reinterpret_cast<uint8_t*>(request); ++ ++ reqptr += sizeof(ipmiI2cRwReq); ++ std::copy(reqptr, reqptr + writeCount, outBuf.begin()); ++ ++ log<level::DEBUG>("checking list ", entry("SIZE=%d", getWhiteList().size())); ++ // command whitelist checking ++ for (unsigned int i = 0; i < getWhiteList().size(); i++) ++ { ++ // TODO add wildchard/regex support ++ if ((busId == getWhiteList()[i].busId) && ++ (i2cAddr == getWhiteList()[i].slaveAddr) && ++ (outBuf == getWhiteList()[i].data)) ++ { ++ log<level::DEBUG>("In whitelist"); ++ foundInList = true; ++ break; ++ } ++ } ++ ++ if (!foundInList) ++ { ++ log<level::ERR>("Request blocked!", entry("BUS=%d", busId), ++ entry("ADDR=0x%x", reqi2c->slaveAddr)); ++ return IPMI_CC_INVALID_FIELD_REQUEST; ++ } ++ ++ log<level::DEBUG>("IPMI Master WriteRead ", entry("BUS=%d", busId), ++ entry("ADDR=0x%x", reqi2c->slaveAddr), ++ entry("R=%d", reqi2c->readCount), ++ entry("W=%d", writeCount)); ++ ++ std::string i2cBus = "/dev/i2c-" + std::to_string(busId); ++ ++ int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC); ++ if (i2cDev < 0) ++ { ++ log<level::ERR>("Failed in opening i2c device", ++ entry("BUS=%s", i2cBus.c_str())); ++ return IPMI_CC_UNSPECIFIED_ERROR; ++ } ++ ++ // write message ++ i2cmsg[0].addr = i2cAddr; ++ i2cmsg[0].flags = 0x00; ++ i2cmsg[0].len = writeCount; ++ i2cmsg[0].buf = outBuf.data(); ++ ++ // read message ++ i2cmsg[1].addr = i2cAddr; ++ i2cmsg[1].flags = I2C_M_RD; ++ i2cmsg[1].len = reqi2c->readCount; ++ i2cmsg[1].buf = inBuf.data(); ++ ++ msgRdwr.msgs = i2cmsg; ++ msgRdwr.nmsgs = 2; ++ ++ ret = ::ioctl(i2cDev, I2C_RDWR, &msgRdwr); ++ ::close(i2cDev); ++ ++ // TODO add completion code support ++ if (ret < 0) ++ { ++ log<level::ERR>("RDWR ioctl error", entry("RET=%d", ret)); ++ return IPMI_CC_UNSPECIFIED_ERROR; ++ } ++ ++ *data_len = msgRdwr.msgs[1].len; ++ std::copy(msgRdwr.msgs[1].buf, msgRdwr.msgs[1].buf + msgRdwr.msgs[1].len, ++ resptr); ++ ++ return IPMI_CC_OK; ++} ++ + void register_netfn_app_functions() + { ++ int ret = -1; ++ + // <Get BT Interface Capabilities> + ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL, + ipmi_app_get_bt_capabilities, PRIVILEGE_USER); +@@ -1145,6 +1370,17 @@ void register_netfn_app_functions() + ipmi_app_channel_info, PRIVILEGE_USER); + #endif + ++ ret = loadI2CWhiteList(); ++ log<level::DEBUG>("i2c white list is loaded", entry("RET=%d", ret), ++ entry("SIZE=%d", getWhiteList().size())); ++ if (ret == 0) ++ { ++ log<level::DEBUG>("Register Master RW command"); ++ // <Master Write Read Command> ++ ipmi_register_callback(NETFUN_APP, IPMI_CMD_MASTER_WRITE_READ, NULL, ++ ipmiMasterWriteRead, PRIVILEGE_OPERATOR); ++ } ++ + // <Get System GUID Command> + ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL, + ipmi_app_get_sys_guid, PRIVILEGE_USER); +diff --git a/apphandler.hpp b/apphandler.hpp +index d4dd8e8..f9e5c59 100644 +--- a/apphandler.hpp ++++ b/apphandler.hpp +@@ -19,6 +19,7 @@ enum ipmi_netfn_app_cmds + IPMI_CMD_SET_CHAN_ACCESS = 0x40, + IPMI_CMD_GET_CHANNEL_ACCESS = 0x41, + IPMI_CMD_GET_CHAN_INFO = 0x42, ++ IPMI_CMD_MASTER_WRITE_READ = 0x52, + IPMI_CMD_GET_CHAN_CIPHER_SUITES = 0x54, + IPMI_CMD_SET_SYSTEM_INFO = 0x58, + IPMI_CMD_GET_SYSTEM_INFO = 0x59, +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index c7eb2d8..22a2a3c 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -25,6 +25,7 @@ + 0x06:0x36 //<App>:<Get BT Interface Capabilities> + 0x06:0x37 //<App>:<Get System GUID> + 0x06:0x42 //<App>:<Get Channel Info Command> ++0x06:0x52 //<App>:<Master Write Read Command> + 0x06:0x54 //<App>:<Get Channel Cipher Suites> + 0x0A:0x10 //<Storage>:<Get FRU Inventory Area Info> + 0x0A:0x11 //<Storage>:<Read FRU Data> +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0049-Fix-Unspecified-error-on-ipmi-restart-cause-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0049-Fix-Unspecified-error-on-ipmi-restart-cause-command.patch new file mode 100644 index 000000000..aba5eb095 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0049-Fix-Unspecified-error-on-ipmi-restart-cause-command.patch @@ -0,0 +1,71 @@ +From 59287a8869b5253a1b4203e0cc8a92f063dcc7e6 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Tue, 25 Sep 2018 16:08:22 +0800 +Subject: [PATCH] Fix "Unspecified error" on ipmi restart cause command + +Needs to convert the dbus value(enum) into ipmi value(uint8) + +Tested by: +ipmitool chassis restart_cause + +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + chassishandler.cpp | 28 ++++++++++++++++++++++++++-- + 1 file changed, 26 insertions(+), 2 deletions(-) + +diff --git a/chassishandler.cpp b/chassishandler.cpp +index 40eb4f5..c3d4931 100644 +--- a/chassishandler.cpp ++++ b/chassishandler.cpp +@@ -106,7 +106,7 @@ static constexpr auto match = "chassis0"; + const static constexpr char* stateHostInterface = + "xyz.openbmc_project.State.Host"; + const static constexpr char* hostRestartCauseInterface = +- "xyz.openbmc_project.State.Host.HostRestartCause"; ++ "xyz.openbmc_project.State.Host"; + const static constexpr char* hostRestartCause = "HostRestartCause"; + const static constexpr char chassisCapIntf[] = + "xyz.openbmc_project.Control.ChassisCapabilities"; +@@ -1764,6 +1764,26 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + return rc; + } + ++namespace restart_cause ++{ ++ ++using namespace sdbusplus::xyz::openbmc_project::State::server; ++ ++std::map<Host::RestartCause, uint8_t> dbusToIpmi = { ++ {Host::RestartCause::Unknown, 0x0}, ++ {Host::RestartCause::IpmiCommand, 0x1}, ++ {Host::RestartCause::ResetButton, 0x2}, ++ {Host::RestartCause::PowerButton, 0x3}, ++ {Host::RestartCause::WatchdogTimer, 0x4}, ++ {Host::RestartCause::OEM, 0x5}, ++ {Host::RestartCause::PowerPolicyAlwaysOn, 0x6}, ++ {Host::RestartCause::PowerPolicyPreviousState, 0x7}, ++ {Host::RestartCause::PEFReset, 0x8}, ++ {Host::RestartCause::PEFPowerCycle, 0x9}, ++ {Host::RestartCause::SoftReset, 0xa}, ++ {Host::RestartCause::RTCWakeup, 0xb}}; ++} // namespace restart_cause ++ + ipmi_ret_t ipmi_chassis_get_sys_restart_cause( + ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, + ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) +@@ -1787,7 +1807,11 @@ ipmi_ret_t ipmi_chassis_get_sys_restart_cause( + ipmi::Value variant = + ipmi::getDbusProperty(bus, hostObject.second, hostObject.first, + hostRestartCauseInterface, hostRestartCause); +- resp->restartCause = variant.get<uint8_t>(); ++ ++ std::string restartCause = ++ sdbusplus::message::variant_ns::get<std::string>(variant); ++ resp->restartCause = restart_cause::dbusToIpmi.at( ++ restart_cause::Host::convertRestartCauseFromString(restartCause)); + } + + catch (std::exception& e) +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0050-enable-6-oem-commands.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0050-enable-6-oem-commands.patch new file mode 100644 index 000000000..b800632cc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0050-enable-6-oem-commands.patch @@ -0,0 +1,15 @@ +diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf +index 22a2a3c..5d71698 100644 +--- a/host-ipmid-whitelist.conf ++++ b/host-ipmid-whitelist.conf +@@ -49,3 +49,10 @@ + 0x30:0x26 //<OEM>:<Set BIOS ID> + 0x30:0x27 //<OEM>:<Get Device Info> + 0x30:0x31 //<OEM>:<Get AIC card FRU> ++0x30:0x54 //<OEM>:<Set Power Restore Delay> ++0x30:0x55 //<OEM>:<Get Power Restore Delay> ++0x30:0x9A //<OEM>:<Get Processor Error Config> ++0x30:0x9B //<OEM>:<Set Processor Error Config> ++0x30:0xB0 //<OEM>:<Get LED Status> ++0x30:0xE9 //<OEM>:<Get BIOS Post Codes> ++ diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0051-Fix-Set-LAN-Config-to-work-without-SetInProgress.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0051-Fix-Set-LAN-Config-to-work-without-SetInProgress.patch new file mode 100644 index 000000000..3990c6b5c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0051-Fix-Set-LAN-Config-to-work-without-SetInProgress.patch @@ -0,0 +1,142 @@ +From cae9e21f88e6f12c80c89402473a17a10258c843 Mon Sep 17 00:00:00 2001 +From: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com> +Date: Thu, 17 Jan 2019 21:22:30 +0530 +Subject: [PATCH] Fix: Set LAN Config to work without SetInProgress + +Set LAN Configuration parameters in up-stream code works +with SetInProgress (parameter selector 0), to be marked +as SET_IN_PROGRESS before fields update, and SET_COMPLETE to +make the changes effective. This is not mandatory as per +IPMI Spec, and we must support individual fields update. +Fix: +1. After SET_COMPLETE for parameter selector, changes has +to be applied immediately, and doesn't require to rely on +network timer, as purpose of this logic itself is to stage +and commit. +2. Allow individual parameter changes to take effect based +on timer. For the time being reduced the timer to 5 sec +to have quicker turn-around and group things together. + +TODO: +Still need to introduce lock between ChannelConfig variable +between Timer & Get / Set LAN Configuration command to avoid +race condition + +Unit-Test: +1. Verified the BIOS Setup page, able to set the IPV4 to static +IP, afte disabling IPV6, and configuring IPV4 to static, after +save and reset, the changes of IPV4 static is preserved. + +Change-Id: I7c2edad2861b5dba5ad1ca97cc5e39ac02871746 +Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com> +--- + transporthandler.cpp | 54 ++++++++++++++++++++++++++++++++++++---------------- + transporthandler.hpp | 2 ++ + 2 files changed, 40 insertions(+), 16 deletions(-) + +Index: phosphor-host-ipmid.clean/transporthandler.cpp +=================================================================== +--- phosphor-host-ipmid.clean.orig/transporthandler.cpp ++++ phosphor-host-ipmid.clean/transporthandler.cpp +@@ -399,6 +399,41 @@ struct set_lan_t + uint8_t data[8]; // Per IPMI spec, not expecting more than this size + } __attribute__((packed)); + ++ipmi_ret_t checkAndUpdateNetwork(int channel) ++{ ++ auto channelConf = getChannelConfig(channel); ++ using namespace std::chrono_literals; ++ // time to wait before applying the network changes. ++ constexpr auto networkTimeout = 5000000us; // 5 sec ++ ++ if (channelConf->lan_set_in_progress == SET_COMPLETE && ++ ((channelConf->flush == false) || ++ (channelConf->updateInProgress == true))) ++ { ++ channelConf->flush = true; ++ // used to indicate that network timer update is in progress. ++ channelConf->updateInProgress = true; ++ if (!networkTimer) ++ { ++ log<level::ERR>("Network timer is not instantiated"); ++ return IPMI_CC_UNSPECIFIED_ERROR; ++ } ++ // start/restart the timer ++ // TODO: Need to implement locking mechansim between networkTimer & ++ // get/set to avoid race condition. ++ networkTimer->start(networkTimeout); ++ } ++ else if (channelConf->lan_set_in_progress == SET_COMPLETE && ++ channelConf->flush == true && ++ channelConf->updateInProgress == false) ++ { ++ // Apply the network changes immediately, if proper SET_IN_PROGRESS, ++ // followed by SET_COMPLETE is issued. ++ applyChanges(channel); ++ } ++ return IPMI_CC_OK; ++} ++ + ipmi_ret_t ipmi_transport_set_lan(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_request_t request, + ipmi_response_t response, +@@ -406,12 +441,6 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + ipmi_context_t context) + { + ipmi_ret_t rc = IPMI_CC_OK; +- +- using namespace std::chrono_literals; +- +- // time to wait before applying the network changes. +- constexpr auto networkTimeout = 10000000us; // 10 sec +- + char ipaddr[INET_ADDRSTRLEN]; + char netmask[INET_ADDRSTRLEN]; + char gateway[INET_ADDRSTRLEN]; +@@ -543,15 +572,6 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + entry("ADDRESS=%s", channelConf->ipaddr.c_str()), + entry("GATEWAY=%s", channelConf->gateway.c_str()), + entry("VLAN=%d", channelConf->vlanID)); +- +- if (!networkTimer) +- { +- log<level::ERR>("Network timer is not instantiated"); +- return IPMI_CC_UNSPECIFIED_ERROR; +- } +- +- // start/restart the timer +- networkTimer->start(networkTimeout); + } + else if (reqptr->data[0] == SET_IN_PROGRESS) // Set In Progress + { +@@ -680,8 +700,10 @@ ipmi_ret_t ipmi_transport_set_lan(ipmi_n + default: + { + rc = IPMI_CC_PARM_NOT_SUPPORTED; ++ return rc; + } + } ++ rc = checkAndUpdateNetwork(channel); + + return rc; + } +Index: phosphor-host-ipmid.clean/transporthandler.hpp +=================================================================== +--- phosphor-host-ipmid.clean.orig/transporthandler.hpp ++++ phosphor-host-ipmid.clean/transporthandler.hpp +@@ -140,6 +140,7 @@ struct ChannelConfig_t + // vlan id is in 12 bits and the 16th bit is for enable mask. + uint32_t vlanID = ipmi::network::VLAN_ID_MASK; + uint8_t lan_set_in_progress = SET_COMPLETE; ++ uint8_t updateInProgress = false; + bool flush = false; + + // IPV6 parameters +@@ -165,6 +166,7 @@ struct ChannelConfig_t + vlanID = ipmi::network::VLAN_ID_MASK; + ipsrc = ipmi::network::IPOrigin::UNSPECIFIED; + lan_set_in_progress = SET_COMPLETE; ++ updateInProgress = false; + flush = false; + + // IPv6 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0053-Fix-keep-looping-issue-when-entering-OS.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0053-Fix-keep-looping-issue-when-entering-OS.patch new file mode 100644 index 000000000..2a4cc9bb1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/0053-Fix-keep-looping-issue-when-entering-OS.patch @@ -0,0 +1,80 @@ +From 9ed3fd11047f8c360b7d808946939ef280813811 Mon Sep 17 00:00:00 2001 +From: Cheng C Yang <cheng.c.yang@linux.intel.com> +Date: Wed, 23 Jan 2019 17:02:40 +0800 +Subject: [PATCH] Fix keep looping issue when entering OS + +Sometimes when entering OS, OS will keep continuously sending ipmi command +"READ EVENT MESSAGE BUFFER" to BMC. This issue is caused by incorrect KCS +status. If restart the host immediately while OS is still running, SMS_ATN +will be set, after that KCS come into an incorrect status, and then KCS +communction between BMC and OS crash. To make KCS go back to correct status +and fix the issue, clear SMS_ATN after every time power cycle happen. + +Unit Test: + After entered OS, force reset system, after enter OS again, OS can start +normally without keep sending READ EVENT MESSAGE BUFFER command. + After power on system, enter EFI SHELL, check cmdtool.efi can work +correctly through KCS channel. +--- + host-cmd-manager.cpp | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/host-cmd-manager.cpp b/host-cmd-manager.cpp +index 0a61e63..6e50684 100644 +--- a/host-cmd-manager.cpp ++++ b/host-cmd-manager.cpp +@@ -26,6 +26,8 @@ constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; + constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0"; + constexpr auto HOST_STATE_INTERFACE = "xyz.openbmc_project.State.Host"; + constexpr auto HOST_TRANS_PROP = "RequestedHostTransition"; ++constexpr const char* IPMI_PATH = "/org/openbmc/HostIpmi/1"; ++constexpr const char* IPMI_INTERFACE = "org.openbmc.HostIpmi"; + + // For throwing exceptions + using namespace phosphor::logging; +@@ -107,6 +109,20 @@ void Manager::clearQueue() + // `false` indicating Failure + std::get<CallBack>(command)(ipmiCmdData, false); + } ++ ++ auto host = ::ipmi::getService(this->bus, IPMI_INTERFACE, IPMI_PATH); ++ auto method = this->bus.new_method_call(host.c_str(), IPMI_PATH, ++ IPMI_INTERFACE, "clearAttention"); ++ ++ try ++ { ++ auto reply = this->bus.call(method); ++ } ++ catch (sdbusplus::exception_t&) ++ { ++ log<level::ERR>("Error in clearing SMS attention"); ++ elog<InternalFailure>(); ++ } + } + + // Called for alerting the host +@@ -116,9 +132,6 @@ void Manager::checkQueueAndAlertHost() + { + log<level::DEBUG>("Asserting SMS Attention"); + +- std::string IPMI_PATH("/org/openbmc/HostIpmi/1"); +- std::string IPMI_INTERFACE("org.openbmc.HostIpmi"); +- + auto host = ::ipmi::getService(this->bus, IPMI_INTERFACE, IPMI_PATH); + + // Start the timer for this transaction +@@ -132,9 +145,8 @@ void Manager::checkQueueAndAlertHost() + return; + } + +- auto method = +- this->bus.new_method_call(host.c_str(), IPMI_PATH.c_str(), +- IPMI_INTERFACE.c_str(), "setAttention"); ++ auto method = this->bus.new_method_call(host.c_str(), IPMI_PATH, ++ IPMI_INTERFACE, "setAttention"); + auto reply = this->bus.call(method); + + if (reply.is_method_error()) +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/phosphor-ipmi-host.service b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/phosphor-ipmi-host.service new file mode 100644 index 000000000..d855eaa5b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host/phosphor-ipmi-host.service @@ -0,0 +1,26 @@ +[Unit] +Description=Phosphor Inband IPMI +# TODO openbmc/openbmc#2059 - The wants/after below should be based on providers +Wants=mapper-wait@-xyz-openbmc_project-control-host0-boot.service +After=mapper-wait@-xyz-openbmc_project-control-host0-boot.service +Wants=mapper-wait@-xyz-openbmc_project-control-host0-boot-one_time.service +After=mapper-wait@-xyz-openbmc_project-control-host0-boot-one_time.service +Wants=mapper-wait@-xyz-openbmc_project-control-host0-power_restore_policy.service +After=mapper-wait@-xyz-openbmc_project-control-host0-power_restore_policy.service +Wants=mapper-wait@-xyz-openbmc_project-control-host0-restriction_mode.service +After=mapper-wait@-xyz-openbmc_project-control-host0-restriction_mode.service +Wants=clear-once.service +After=clear-once.service + +[Service] +Restart=always +RestartSec=5 +StartLimitBurst=10 +ExecStart=/usr/bin/env ipmid +SyslogIdentifier=ipmid +RuntimeDirectory = ipmi +RuntimeDirectoryPreserve = yes +StateDirectory = ipmi + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend new file mode 100644 index 000000000..a92fc833b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-host_%.bbappend @@ -0,0 +1,29 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://phosphor-ipmi-host.service \ + file://0002-Modify-dbus-interface-for-power-control.patch \ + file://0003-Modify-dbus-interface-for-chassis-control.patch \ + file://0009-IPv6-Network-changes.patch \ + file://0010-fix-get-system-GUID-ipmi-command.patch \ + file://0012-ipmi-set-get-boot-options.patch \ + file://0013-ipmi-add-set-bios-id-to-whitelist.patch \ + file://0014-Enable-get-device-guid-ipmi-command.patch \ + file://0016-add-better-sdbusplus-exception-handling.patch \ + file://0018-Catch-sdbusplus-exceptions-in-IPMI-net.patch \ + file://0021-Implement-IPMI-Commmand-Get-Host-Restart-Cause.patch \ + file://0039-ipmi-add-oem-command-get-AIC-FRU-to-whitelist.patch \ + file://0048-Implement-IPMI-Master-Write-Read-command.patch \ + file://0049-Fix-Unspecified-error-on-ipmi-restart-cause-command.patch \ + file://0050-enable-6-oem-commands.patch \ + file://0051-Fix-Set-LAN-Config-to-work-without-SetInProgress.patch \ + file://0053-Fix-keep-looping-issue-when-entering-OS.patch \ + " + +do_install_append(){ + install -d ${D}${includedir}/phosphor-ipmi-host + install -d ${D}${libdir}/phosphor-ipmi-host + install -m 0644 -D ${S}/*.h ${D}${includedir}/phosphor-ipmi-host + install -m 0644 -D ${S}/*.hpp ${D}${includedir}/phosphor-ipmi-host + install -m 0644 -D ${S}/utils.cpp ${D}${libdir}/phosphor-ipmi-host + +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb_%.bbappend new file mode 100644 index 000000000..d5d38a0ce --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-ipmb_%.bbappend @@ -0,0 +1,2 @@ +SRC_URI = "git://github.com/openbmc/ipmbbridge.git" +SRCREV = "25e85c79257723b1cb754c20299196685373ce24" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/org.openbmc.HostIpmi.SMM.service b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/org.openbmc.HostIpmi.SMM.service new file mode 100644 index 000000000..288fa422d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/org.openbmc.HostIpmi.SMM.service @@ -0,0 +1,13 @@ +[Unit] +Description=Phosphor IPMI KCS DBus Bridge(SMM) +After=phosphor-ipmi-host.service + +[Service] +Restart=always +ExecStart={sbindir}/kcsbridged --d="/dev/ipmi-kcs4" --i="SMM" +SyslogIdentifier=kcsbridged_SMM +Type=dbus +BusName={BUSNAME} + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/org.openbmc.HostIpmi.service b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/org.openbmc.HostIpmi.service new file mode 100644 index 000000000..177062e27 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs/org.openbmc.HostIpmi.service @@ -0,0 +1,13 @@ +[Unit] +Description=Phosphor IPMI KCS DBus Bridge(SMS) +After=phosphor-ipmi-host.service + +[Service] +Restart=always +ExecStart={sbindir}/kcsbridged --d="/dev/ipmi-kcs3" +SyslogIdentifier=kcsbridged +Type=dbus +BusName={BUSNAME} + +[Install] +WantedBy={SYSTEMD_DEFAULT_TARGET} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs_%.bbappend new file mode 100644 index 000000000..ac7a03108 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-kcs_%.bbappend @@ -0,0 +1,9 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +DBUS_SERVICE_${PN} += "org.openbmc.HostIpmi.SMM.service" + +SYSTEMD_SUBSTITUTIONS_remove = "KCS_DEVICE:${KCS_DEVICE}:${DBUS_SERVICE_${PN}}" + +SRC_URI = "git://github.com/openbmc/kcsbridge.git" +SRCREV = "17a2ab7f39a78ff0603aa68cf35108ea94eb442f" + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0006-Modify-dbus-namespace-of-chassis-control-for-guid.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0006-Modify-dbus-namespace-of-chassis-control-for-guid.patch new file mode 100644 index 000000000..7225c7529 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0006-Modify-dbus-namespace-of-chassis-control-for-guid.patch @@ -0,0 +1,39 @@ +From 6fc55bb689272d34ff6616cdd4b24367ea39c749 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Mon, 2 Jul 2018 15:51:52 +0800 +Subject: [PATCH] Modify dbus namespace of chassis control for guid.cpp +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Switch chassis control service namespace for guid.cpp from “org” to “xyz”, +to compatible with new intel-chassis services + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + command/guid.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +Index: phosphor-net-ipmid.clean/command/guid.cpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/command/guid.cpp ++++ phosphor-net-ipmid.clean/command/guid.cpp +@@ -21,7 +21,8 @@ namespace command + + std::unique_ptr<sdbusplus::bus::match_t> matchPtr(nullptr); + +-static constexpr auto guidObjPath = "/org/openbmc/control/chassis0"; ++static constexpr auto guidObjPath = ++ "/xyz/openbmc_project/Chassis/Control/Chassis0"; + static constexpr auto propInterface = "org.freedesktop.DBus.Properties"; + + Guid getSystemGUID() +@@ -31,7 +32,7 @@ Guid getSystemGUID() + Guid guid = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}; + +- constexpr auto chassisIntf = "org.openbmc.control.Chassis"; ++ constexpr auto chassisIntf = "xyz.openbmc_project.Chassis.Control.Chassis"; + + sd_bus_message* reply = nullptr; + sd_bus_error error = SD_BUS_ERROR_NULL; diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0007-Adding-support-for-GetSessionInfo-command.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0007-Adding-support-for-GetSessionInfo-command.patch new file mode 100644 index 000000000..fda7ed2ca --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0007-Adding-support-for-GetSessionInfo-command.patch @@ -0,0 +1,421 @@ +From f5c7d30be4a097998d9390614c0faa2d77109ca5 Mon Sep 17 00:00:00 2001 +From: ssekar <suryakanth.sekar@linux.intel.com> +Date: Wed, 12 Dec 2018 16:04:15 +0530 +Subject: [PATCH] Adding support for GetSessionInfo command + +Description: user can get all session info (remote ip,port, +session id, priv, etc) using this command. + +Verification :we can get all active and non active session +info by session handle session id. +Updated the Remote IP addr and Port update for sessioninfo. +Unit testing are done. + +Change-Id: I662ef2b9f0c1d6bda331eb6481d7b9f34534541b +Signed-off-by: ssekar <suryakanth.sekar@linux.intel.com> +--- + comm_module.cpp | 8 +++ + command/session_cmds.cpp | 147 +++++++++++++++++++++++++++++++++++++++ + command/session_cmds.hpp | 55 +++++++++++++++ + message_handler.cpp | 1 + + sessions_manager.cpp | 55 +++++++++++++++ + sessions_manager.hpp | 7 ++ + socket_channel.cpp | 27 ++++++- + socket_channel.hpp | 3 +- + 8 files changed, 301 insertions(+), 2 deletions(-) + +Index: phosphor-net-ipmid.clean/comm_module.cpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/comm_module.cpp ++++ phosphor-net-ipmid.clean/comm_module.cpp +@@ -53,6 +53,14 @@ void sessionSetupCommands() + &closeSession, + session::Privilege::CALLBACK, + false}, ++ // Session Info Command ++ { ++ { ++ (static_cast<uint32_t>(message::PayloadType::IPMI) << 16) | ++ static_cast<uint16_t>(command::NetFns::APP) | 0x3D ++ }, ++ &getSessionInfo, session::Privilege::USER, false ++ }, + }; + + for (auto& iter : commands) +Index: phosphor-net-ipmid.clean/command/session_cmds.cpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/command/session_cmds.cpp ++++ phosphor-net-ipmid.clean/command/session_cmds.cpp +@@ -5,11 +5,19 @@ + + #include <host-ipmid/ipmid-api.h> + ++#include <iostream> + #include <user_channel/channel_layer.hpp> + #include <user_channel/user_layer.hpp> + + namespace command + { ++// Defined as per IPMI sepcification ++static constexpr uint8_t searchCurrentSession = 0x00; ++static constexpr uint8_t searchSessionByHandle = 0xFE; ++static constexpr uint8_t searchSessionByID = 0xFF; ++ ++static constexpr uint8_t ipmi15VerSession = 0x00; ++static constexpr uint8_t ipmi20VerSession = 0x01; + + std::vector<uint8_t> + setSessionPrivilegeLevel(const std::vector<uint8_t>& inPayload, +@@ -110,4 +118,143 @@ std::vector<uint8_t> closeSession(const + return outPayload; + } + ++std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, ++ const message::Handler& handler) ++ ++{ ++ std::vector<uint8_t> outPayload(sizeof(GetSessionInfoResponse)); ++ auto request = ++ reinterpret_cast<const GetSessionInfoRequest*>(inPayload.data()); ++ auto response = ++ reinterpret_cast<GetSessionInfoResponse*>(outPayload.data()); ++ uint32_t reqSessionID = handler.sessionID; ++ response->completionCode = IPMI_CC_OK; ++ if (inPayload.size() == 1 && request->sessionIndex != 0) ++ { ++ if (request->sessionIndex <= session::MAX_SESSION_COUNT) ++ { ++ reqSessionID = std::get<session::Manager&>(singletonPool) ++ .getSessionIDbyHandle(request->sessionIndex); ++ } ++ else ++ { ++ response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ } ++ ++ // Here we look for session info according to session index parameter ++ switch (request->sessionIndex) ++ { ++ // Look for current active session which this cmd is received over ++ case searchCurrentSession: ++ // Request data should only contain session index byte ++ if (inPayload.size() != 1) ++ { ++ response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ // To look for current active session which the command came over, ++ // the session ID cannot be 0. ++ if (0 == reqSessionID) ++ { ++ response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ break; ++ case searchSessionByHandle: ++ // Request data should only contain session index byte and Session ++ // handle ++ if (inPayload.size() != 2) ++ { ++ response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ ++ // Retrieve session id based on session handle ++ if (request->sessionHandle <= session::MAX_SESSION_COUNT) ++ { ++ reqSessionID = ++ std::get<session::Manager&>(singletonPool) ++ .getSessionIDbyHandle(request->sessionHandle); ++ } ++ else ++ { ++ response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ break; ++ case searchSessionByID: ++ // Request data should only contain session index byte and Session ++ // handle ++ if (inPayload.size() != sizeof(GetSessionInfoRequest)) ++ { ++ response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ reqSessionID = endian::from_ipmi(request->sessionID); ++ ++ break; ++ default: ++ if (inPayload.size() != 1) ++ { ++ response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ } ++ ++ response->totalSessionCount = session::MAX_SESSION_COUNT; ++ response->activeSessioncount = ++ std::get<session::Manager&>(singletonPool).getNoOfActiveSession(); ++ response->sessionHandle = 0; ++ if (reqSessionID != 0) ++ { ++ ++ std::shared_ptr<session::Session> sessionInfo; ++ try ++ { ++ sessionInfo = std::get<session::Manager&>(singletonPool) ++ .getSession(reqSessionID); ++ } ++ catch (std::exception& e) ++ { ++ response->completionCode = IPMI_CC_UNSPECIFIED_ERROR; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ response->sessionHandle = std::get<session::Manager&>(singletonPool) ++ .getSessionHandle(reqSessionID); ++ uint8_t userId = ipmi::ipmiUserGetUserId(sessionInfo->userName); ++ if (userId == ipmi::invalidUserId) ++ { ++ response->completionCode = IPMI_CC_UNSPECIFIED_ERROR; ++ outPayload.resize(sizeof(response->completionCode)); ++ return std::move(outPayload); ++ } ++ response->userID = userId; // userId; ++ response->privLevel = static_cast<uint8_t>(sessionInfo->curPrivLevel); ++ response->chanNum = sessionInfo->chNum; // byte7 3:0 ++ response->ipmiVer = ipmi20VerSession; // byte7 7:4 ++ response->remoteIpAddr = ++ sessionInfo->channelPtr->getRemoteAddressInbytes(); ++ response->remotePort = ++ sessionInfo->channelPtr->getPort(); // remoteSessionPort; ++ ++ std::cerr << "\nSessionInfo:" << (int)reqSessionID; ++ // TODO: Filling the Remote MACAddress ++ } ++ else ++ { ++ outPayload.resize(4); ++ } ++ return std::move(outPayload); ++} ++ + } // namespace command +Index: phosphor-net-ipmid.clean/command/session_cmds.hpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/command/session_cmds.hpp ++++ phosphor-net-ipmid.clean/command/session_cmds.hpp +@@ -116,4 +116,59 @@ struct CloseSessionResponse + std::vector<uint8_t> closeSession(const std::vector<uint8_t>& inPayload, + const message::Handler& handler); + ++/** ++ * @struct GetSessionInfoRequest ++ * ++ * IPMI Request data for getSession info command ++ */ ++struct GetSessionInfoRequest ++{ ++ uint8_t sessionIndex; ++ union ++ { ++ uint8_t sessionHandle; ++ uint32_t sessionID; ++ }; ++} __attribute__((packed)); ++ ++/** ++ * @struct getSessionInfoResponse ++ * ++ * IPMI Response data for getSession info command ++ */ ++struct GetSessionInfoResponse ++{ ++ uint8_t completionCode; ++ uint8_t sessionHandle; ++ uint8_t totalSessionCount; ++ uint8_t activeSessioncount; ++ uint8_t userID; ++ uint8_t privLevel; ++#if BYTE_ORDER == LITTLE_ENDIAN ++ uint8_t chanNum : 4; ++ uint8_t ipmiVer : 4; ++#endif ++#if BYTE_ORDER == BIG_ENDIAN ++ uint8_t ipmiVer : 4; ++ uint8_t chanNum : 4; ++#endif ++ uint32_t remoteIpAddr; // for channel private data ++ uint8_t remoteMACAddr[6]; ++ uint16_t remotePort; ++} __attribute__((packed)); ++ ++/** ++ * @brief GetSessionInfo Command ++ * ++ * This command is used to get the session information based on ++ * session handle or session ID. Retreive all session information. ++ ++ * @param[in] inPayload - Request Data for the command ++ * @param[in] handler - Reference to the Message Handler ++ * ++ * @return Response data for the command ++ */ ++std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, ++ const message::Handler& handler); ++ + } // namespace command +Index: phosphor-net-ipmid.clean/message_handler.cpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/message_handler.cpp ++++ phosphor-net-ipmid.clean/message_handler.cpp +@@ -43,6 +43,7 @@ std::shared_ptr<Message> Handler::receiv + sessionID = message->bmcSessionID; + message->rcSessionID = session->getRCSessionID(); + session->updateLastTransactionTime(); ++ session->channelPtr = channel; + + return message; + } +Index: phosphor-net-ipmid.clean/sessions_manager.cpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/sessions_manager.cpp ++++ phosphor-net-ipmid.clean/sessions_manager.cpp +@@ -88,6 +88,9 @@ std::shared_ptr<Session> + } + sessionID = session->getBMCSessionID(); + sessionsMap.emplace(sessionID, session); ++ storeSessionHandle(sessionID); ++ ++ + return session; + } + +@@ -149,12 +152,15 @@ std::shared_ptr<Session> Manager::getSes + + void Manager::cleanStaleEntries() + { ++ uint8_t sessionIndex = 0; + for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();) + { + auto session = iter->second; + if ((session->getBMCSessionID() != SESSION_ZERO) && + !(session->isSessionActive())) + { ++ sessionIndex = getSessionHandle(session->getBMCSessionID()); ++ sessionHandleMap[sessionIndex] = 0; + iter = sessionsMap.erase(iter); + } + else +@@ -164,4 +170,53 @@ void Manager::cleanStaleEntries() + } + } + ++uint8_t Manager::storeSessionHandle(SessionID bmcSessionID) ++{ ++ // Zero handler is reserved for invalid session. ++ //index starts with 1, for direct usage. Index 0 reserved ++ for (uint8_t i = 1; i <= MAX_SESSION_COUNT; i++) ++ { ++ if (sessionHandleMap[i] == 0) ++ { ++ sessionHandleMap[i] = bmcSessionID; ++ break; ++ } ++ } ++ return 0; ++} ++ ++uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const ++{ ++ if (sessionHandle <= MAX_SESSION_COUNT) ++ { ++ return sessionHandleMap[sessionHandle]; ++ } ++ return 0; ++} ++ ++uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const ++{ ++ ++ for (uint8_t i = 1; i <= MAX_SESSION_COUNT; i++) ++ { ++ if (sessionHandleMap[i] == bmcSessionID) ++ { ++ return i; ++ } ++ } ++ return 0; ++} ++uint8_t Manager::getNoOfActiveSession() const ++{ ++ uint8_t count = 0; ++ for (const auto& it : sessionsMap) ++ { ++ const auto& session = it.second; ++ if (session->state == State::ACTIVE) ++ { ++ count++; ++ } ++ } ++ return count; ++} + } // namespace session +Index: phosphor-net-ipmid.clean/sessions_manager.hpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/sessions_manager.hpp ++++ phosphor-net-ipmid.clean/sessions_manager.hpp +@@ -82,8 +82,15 @@ class Manager + std::shared_ptr<Session> + getSession(SessionID sessionID, + RetrieveOption option = RetrieveOption::BMC_SESSION_ID); ++ uint8_t getNoOfActiveSession() const; ++ uint8_t getSessionHandle(SessionID bmcSessionID) const; ++ uint8_t storeSessionHandle(SessionID bmcSessionID); ++ uint32_t getSessionIDbyHandle(uint8_t sessionHandle) const; + + private: ++ //+1 for session, as 0 is reserved for sessionless command ++ std::array<uint32_t, MAX_SESSION_COUNT + 1> sessionHandleMap; ++ + /** + * @brief Session Manager keeps the session objects as a sorted + * associative container with Session ID as the unique key +Index: phosphor-net-ipmid.clean/socket_channel.hpp +=================================================================== +--- phosphor-net-ipmid.clean.orig/socket_channel.hpp ++++ phosphor-net-ipmid.clean/socket_channel.hpp +@@ -65,6 +65,23 @@ class Channel + } + + /** ++ * @brief Return the binary representation of the remote IPv4 address ++ * ++ * getSessionInfo needs to return the remote IPv4 addresses of each session ++ * ++ * @return A uint32_t representation of the remote IPv4 address ++ */ ++ std::uint32_t getRemoteAddressInbytes() ++ { ++ const boost::asio::ip::address& addr = endpoint.address(); ++ if (addr.is_v4()) ++ { ++ return addr.to_v4().to_uint(); ++ } ++ return 0; ++ } ++ ++ /** + * @brief Read the incoming packet + * + * Reads the data available on the socket diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0008-Sync-GetSession-Info-cmd-based-on-Upstream-review.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0008-Sync-GetSession-Info-cmd-based-on-Upstream-review.patch new file mode 100644 index 000000000..1a109a571 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net/0008-Sync-GetSession-Info-cmd-based-on-Upstream-review.patch @@ -0,0 +1,318 @@ +From 0ecc7c816ad4836f8f54922ba92cb527f5978d5a Mon Sep 17 00:00:00 2001 +From: Suryakanth Sekar <suryakanth.sekar@linux.intel.com> +Date: Wed, 6 Mar 2019 10:35:56 +0530 +Subject: [PATCH] Sync GetSession Info cmd based on Upstream review + +Signed-off-by: Suryakanth Sekar <suryakanth.sekar@linux.intel.com> +--- + comm_module.cpp | 12 ++++---- + command/session_cmds.cpp | 72 +++++++++++++++++++++--------------------------- + sessions_manager.cpp | 10 +++---- + sessions_manager.hpp | 2 +- + socket_channel.hpp | 33 +++++++++++----------- + 5 files changed, 59 insertions(+), 70 deletions(-) + +diff --git a/comm_module.cpp b/comm_module.cpp +index 7a1a17d..2546583 100644 +--- a/comm_module.cpp ++++ b/comm_module.cpp +@@ -54,13 +54,11 @@ void sessionSetupCommands() + session::Privilege::CALLBACK, + false}, + // Session Info Command +- { +- { +- (static_cast<uint32_t>(message::PayloadType::IPMI) << 16) | +- static_cast<uint16_t>(command::NetFns::APP) | 0x3D +- }, +- &getSessionInfo, session::Privilege::USER, false +- }, ++ {{(static_cast<uint32_t>(message::PayloadType::IPMI) << 16) | ++ static_cast<uint16_t>(command::NetFns::APP) | 0x3D}, ++ &getSessionInfo, ++ session::Privilege::USER, ++ false}, + }; + + for (auto& iter : commands) +diff --git a/command/session_cmds.cpp b/command/session_cmds.cpp +index 7563b18..fc996a4 100644 +--- a/command/session_cmds.cpp ++++ b/command/session_cmds.cpp +@@ -5,13 +5,12 @@ + + #include <ipmid/api.h> + +-#include <iostream> + #include <user_channel/channel_layer.hpp> + #include <user_channel/user_layer.hpp> + + namespace command + { +-// Defined as per IPMI sepcification ++// Defined as per IPMI specification + static constexpr uint8_t searchCurrentSession = 0x00; + static constexpr uint8_t searchSessionByHandle = 0xFE; + static constexpr uint8_t searchSessionByID = 0xFF; +@@ -129,20 +128,6 @@ std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, + reinterpret_cast<GetSessionInfoResponse*>(outPayload.data()); + uint32_t reqSessionID = handler.sessionID; + response->completionCode = IPMI_CC_OK; +- if (inPayload.size() == 1 && request->sessionIndex != 0) +- { +- if (request->sessionIndex <= session::MAX_SESSION_COUNT) +- { +- reqSessionID = std::get<session::Manager&>(singletonPool) +- .getSessionIDbyHandle(request->sessionIndex); +- } +- else +- { +- response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; +- outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); +- } +- } + + // Here we look for session info according to session index parameter + switch (request->sessionIndex) +@@ -150,29 +135,22 @@ std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, + // Look for current active session which this cmd is received over + case searchCurrentSession: + // Request data should only contain session index byte +- if (inPayload.size() != 1) ++ if (inPayload.size() != sizeof(request->sessionIndex)) + { + response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); +- } +- // To look for current active session which the command came over, +- // the session ID cannot be 0. +- if (0 == reqSessionID) +- { +- response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; +- outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + break; + case searchSessionByHandle: + // Request data should only contain session index byte and Session + // handle +- if (inPayload.size() != 2) ++ if (inPayload.size() != (sizeof(request->sessionIndex) + ++ sizeof(request->sessionHandle))) + { + response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + + // Retrieve session id based on session handle +@@ -186,7 +164,7 @@ std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, + { + response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + break; + case searchSessionByID: +@@ -196,23 +174,38 @@ std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, + { + response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + reqSessionID = endian::from_ipmi(request->sessionID); + + break; + default: +- if (inPayload.size() != 1) ++ if (inPayload.size() == sizeof(request->sessionIndex)) ++ { ++ if (request->sessionIndex <= session::MAX_SESSION_COUNT) ++ { ++ reqSessionID = ++ std::get<session::Manager&>(singletonPool) ++ .getSessionIDbyHandle(request->sessionIndex); ++ } ++ else ++ { ++ response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; ++ outPayload.resize(sizeof(response->completionCode)); ++ return outPayload; ++ } ++ } ++ else + { + response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + } + + response->totalSessionCount = session::MAX_SESSION_COUNT; + response->activeSessioncount = +- std::get<session::Manager&>(singletonPool).getNoOfActiveSession(); ++ std::get<session::Manager&>(singletonPool).getActiveSessionCount(); + response->sessionHandle = 0; + if (reqSessionID != 0) + { +@@ -225,9 +218,9 @@ std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, + } + catch (std::exception& e) + { +- response->completionCode = IPMI_CC_UNSPECIFIED_ERROR; ++ response->completionCode = IPMI_CC_REQ_DATA_LEN_INVALID; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + response->sessionHandle = std::get<session::Manager&>(singletonPool) + .getSessionHandle(reqSessionID); +@@ -236,25 +229,24 @@ std::vector<uint8_t> getSessionInfo(const std::vector<uint8_t>& inPayload, + { + response->completionCode = IPMI_CC_UNSPECIFIED_ERROR; + outPayload.resize(sizeof(response->completionCode)); +- return std::move(outPayload); ++ return outPayload; + } + response->userID = userId; // userId; + response->privLevel = static_cast<uint8_t>(sessionInfo->curPrivLevel); + response->chanNum = sessionInfo->chNum; // byte7 3:0 + response->ipmiVer = ipmi20VerSession; // byte7 7:4 +- response->remoteIpAddr = +- sessionInfo->channelPtr->getRemoteAddressInbytes(); + response->remotePort = + sessionInfo->channelPtr->getPort(); // remoteSessionPort; ++ response->remoteIpAddr = ++ sessionInfo->channelPtr->getRemoteAddressInBytes(); + +- std::cerr << "\nSessionInfo:" << (int)reqSessionID; + // TODO: Filling the Remote MACAddress + } + else + { + outPayload.resize(4); + } +- return std::move(outPayload); ++ return outPayload; + } + + } // namespace command +diff --git a/sessions_manager.cpp b/sessions_manager.cpp +index 9f3210b..c6897c6 100644 +--- a/sessions_manager.cpp ++++ b/sessions_manager.cpp +@@ -152,15 +152,13 @@ std::shared_ptr<Session> Manager::getSession(SessionID sessionID, + + void Manager::cleanStaleEntries() + { +- uint8_t sessionIndex = 0; + for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();) + { + auto session = iter->second; + if ((session->getBMCSessionID() != SESSION_ZERO) && + !(session->isSessionActive())) + { +- sessionIndex = getSessionHandle(session->getBMCSessionID()); +- sessionHandleMap[sessionIndex] = 0; ++ sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0; + iter = sessionsMap.erase(iter); + } + else +@@ -172,8 +170,8 @@ void Manager::cleanStaleEntries() + + uint8_t Manager::storeSessionHandle(SessionID bmcSessionID) + { +- // Zero handler is reserved for invalid session. +- //index starts with 1, for direct usage. Index 0 reserved ++ // Handler index 0 is reserved for invalid session. ++ // index starts with 1, for direct usage. Index 0 reserved + for (uint8_t i = 1; i <= MAX_SESSION_COUNT; i++) + { + if (sessionHandleMap[i] == 0) +@@ -206,7 +204,7 @@ uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const + } + return 0; + } +-uint8_t Manager::getNoOfActiveSession() const ++uint8_t Manager::getActiveSessionCount() const + { + uint8_t count = 0; + for (const auto& it : sessionsMap) +diff --git a/sessions_manager.hpp b/sessions_manager.hpp +index c4caad4..3a3825d 100644 +--- a/sessions_manager.hpp ++++ b/sessions_manager.hpp +@@ -82,7 +82,7 @@ class Manager + std::shared_ptr<Session> + getSession(SessionID sessionID, + RetrieveOption option = RetrieveOption::BMC_SESSION_ID); +- uint8_t getNoOfActiveSession() const; ++ uint8_t getActiveSessionCount() const; + uint8_t getSessionHandle(SessionID bmcSessionID) const; + uint8_t storeSessionHandle(SessionID bmcSessionID); + uint32_t getSessionIDbyHandle(uint8_t sessionHandle) const; +diff --git a/socket_channel.hpp b/socket_channel.hpp +index 349701e..8b64740 100644 +--- a/socket_channel.hpp ++++ b/socket_channel.hpp +@@ -52,33 +52,34 @@ class Channel + } + + /** +- * @brief Fetch the port number of the remote peer +- * +- * Returns the port number of the remote peer ++ * @brief Fetch the IP address of the remote peer + * +- * @return Port number ++ * Returns the IP address of the remote peer which is connected to this ++ * socket + * ++ * @return IP address of the remote peer + */ +- auto getPort() const ++ std::uint32_t getRemoteAddressInBytes() const + { +- return endpoint.port(); ++ const boost::asio::ip::address& addr = endpoint.address(); ++ if (addr.is_v4()) ++ { ++ return addr.to_v4().to_uint(); ++ } ++ return 0; + } + + /** +- * @brief Return the binary representation of the remote IPv4 address ++ * @brief Fetch the port number of the remote peer + * +- * getSessionInfo needs to return the remote IPv4 addresses of each session ++ * Returns the port number of the remote peer ++ * ++ * @return Port number + * +- * @return A uint32_t representation of the remote IPv4 address + */ +- std::uint32_t getRemoteAddressInbytes() ++ auto getPort() const + { +- const boost::asio::ip::address& addr = endpoint.address(); +- if (addr.is_v4()) +- { +- return addr.to_v4().to_uint(); +- } +- return 0; ++ return endpoint.port(); + } + + /** +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net_%.bbappend new file mode 100644 index 000000000..19fa4c06b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-net_%.bbappend @@ -0,0 +1,13 @@ +inherit useradd + +USERADD_PACKAGES = "${PN}" +# add a group called ipmi +GROUPADD_PARAM_${PN} = "ipmi " + +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += " file://0006-Modify-dbus-namespace-of-chassis-control-for-guid.patch \ + file://0007-Adding-support-for-GetSessionInfo-command.patch \ + file://0008-Sync-GetSession-Info-cmd-based-on-Upstream-review.patch \ + " + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-node-manager-proxy_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-node-manager-proxy_git.bb new file mode 100644 index 000000000..24b1dd2a0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-node-manager-proxy_git.bb @@ -0,0 +1,19 @@ +SUMMARY = "Node Manager Proxy" +DESCRIPTION = "The Node Manager Proxy provides a simple interface for communicating \ +with Management Engine via IPMB" + +SRC_URI = "git://git@github.com/openbmc-intel/node-manager;protocol=ssh" +SRCREV = "596cd421d4749c8b6d672fb410eccf9f2da08b3a" +PV = "0.1+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SYSTEMD_SERVICE_${PN} = "node-manager-proxy.service" + +DEPENDS = "sdbusplus \ + phosphor-logging \ + boost" + +S = "${WORKDIR}/git/" +inherit cmake systemd diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%.bbappend new file mode 100644 index 000000000..72d991c7e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%.bbappend @@ -0,0 +1 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%/config.yaml b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%/config.yaml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-ipmi-sensor-inventory%/config.yaml diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-node-manager-proxy_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-node-manager-proxy_git.bb new file mode 100644 index 000000000..808cee1ed --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/ipmi/phosphor-node-manager-proxy_git.bb @@ -0,0 +1,19 @@ +SUMMARY = "Node Manager Proxy" +DESCRIPTION = "The Node Manager Proxy provides a simple interface for communicating \ +with Management Engine via IPMB" + +SRC_URI = "git://git-amr-2.devtools.intel.com:29418/openbmc-node-manager;protocol=ssh" +SRCREV = "e5a5f6189ce357438f40116717b995bab82c50ae" +PV = "0.1+git${SRCPV}" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SYSTEMD_SERVICE_${PN} = "node-manager-proxy.service" + +DEPENDS = "sdbusplus \ + phosphor-logging \ + boost" + +S = "${WORKDIR}/git/" +inherit cmake systemd diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config-native.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config-native.bb new file mode 100644 index 000000000..dd48df0c6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config-native.bb @@ -0,0 +1,21 @@ +SUMMARY = "Phosphor LED Group Management for Intel" +PR = "r1" + +inherit native +inherit obmc-phosphor-utils + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +PROVIDES += "virtual/phosphor-led-manager-config-native" + +SRC_URI += "file://led.yaml" +S = "${WORKDIR}" + +# Overwrite the example led layout yaml file prior +# to building the phosphor-led-manager package +do_install() { + SRC=${S} + DEST=${D}${datadir}/phosphor-led-manager + install -D ${SRC}/led.yaml ${DEST}/led.yaml +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config/led.yaml b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config/led.yaml new file mode 100755 index 000000000..813ffbfd4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/leds/intel-led-manager-config/led.yaml @@ -0,0 +1,36 @@ +bmc_booted: + +power_on: + +status_ok: + status_green: + Action: 'On' + status_amber: + Action: 'Off' + +status_degraded: + status_green: + Action: 'Blink' + DutyOn: 50 + Period: 1000 + status_amber: + Action: 'Off' + +status_non_critical: + status_green: + Action: 'Off' + status_amber: + Action: 'Blink' + DutyOn: 50 + Period: 1000 + +status_critical: + status_green: + Action: 'Off' + status_amber: + Action: 'On' + +enclosure_identify: + identify: + Action: 'On' + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/logging/phosphor-logging_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/logging/phosphor-logging_%.bbappend new file mode 100644 index 000000000..b1f4c1ce5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/logging/phosphor-logging_%.bbappend @@ -0,0 +1,2 @@ +SRCREV = "30047bf9647215951ba5dfe21ceb3e58a1b405a4" + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sel-logger/phosphor-sel-logger_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/sel-logger/phosphor-sel-logger_%.bbappend new file mode 100644 index 000000000..809d05b94 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sel-logger/phosphor-sel-logger_%.bbappend @@ -0,0 +1,6 @@ +# Enable downstream autobump +SRC_URI = "git://github.com/openbmc/phosphor-sel-logger.git" +SRCREV = "2b9704d7eb666c945c73dd74a426a0af2292b0ea" + +# Enable threshold monitoring +EXTRA_OECMAKE += "-DSEL_LOGGER_MONITOR_THRESHOLD_EVENTS=ON" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test/xyz.openbmc_project.selftest.service b/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test/xyz.openbmc_project.selftest.service new file mode 100644 index 000000000..b8c3554ae --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test/xyz.openbmc_project.selftest.service @@ -0,0 +1,10 @@ +[Unit] +Description= BMC Self-Test + +[Service] +Restart=always +ExecStart=/usr/bin/env selftest +SyslogIdentifier=selftest + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test_git.bb new file mode 100644 index 000000000..da1d74207 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/selftest/intel-self-test_git.bb @@ -0,0 +1,38 @@ +SUMMARY = "BMC Self Test service" +DESCRIPTION = "BMC Self Test service for subsystem diagnosis failure info" + +SRC_URI = "git://git@github.com/Intel-BMC/intel-self-test;protocol=ssh" + +PV = "1.0+git${SRCPV}" +SRCREV = "d039998ad2c55aeae4191af30e15bbd3032508c1" + +S = "${WORKDIR}/git" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=fa818a259cbed7ce8bc2a22d35a464fc" + +inherit cmake +inherit obmc-phosphor-dbus-service +inherit obmc-phosphor-systemd +inherit pkgconfig pythonnative + +SYSTEMD_SERVICE_${PN} += "xyz.openbmc_project.selftest.service" + +DEPENDS += " \ + autoconf-archive-native \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-logging \ + phosphor-dbus-interfaces \ + phosphor-dbus-interfaces-native \ + " + +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-logging \ + phosphor-dbus-interfaces \ + " + +EXTRA_OECMAKE = " -DENABLE_GTEST=OFF -DCMAKE_SKIP_RPATH=ON" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend new file mode 100644 index 000000000..56fb8531d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend @@ -0,0 +1,8 @@ +SRCREV = "46342ec359c8e0ed543ebb352cfba8f26ce85afe" +SRC_URI = "git://github.com/openbmc/dbus-sensors.git" + +DEPENDS_append = " i2c-tools" + +# turn this back on when we have a need, but disable it now +# as no shipping platforms use it and it will take cpu cycles +EXTRA_OECMAKE_append = " -DDISABLE_INTRUSION=ON" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-defaults-native.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-defaults-native.bbappend new file mode 100644 index 000000000..436623234 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-defaults-native.bbappend @@ -0,0 +1,4 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://defaults.yaml \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-defaults-native/defaults.yaml b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-defaults-native/defaults.yaml new file mode 100644 index 000000000..24816fb4c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-defaults-native/defaults.yaml @@ -0,0 +1,181 @@ +/xyz/openbmc_project/control/minimum_ship_level_required: + - Interface: xyz.openbmc_project.Control.MinimumShipLevel + Properties: + MinimumShipLevelRequired: + Default: 'true' + +/xyz/openbmc_project/control/host0/auto_reboot: + - Interface: xyz.openbmc_project.Control.Boot.RebootPolicy + Properties: + AutoReboot: + Default: 'false' + +/xyz/openbmc_project/control/host0/boot: + - Interface: xyz.openbmc_project.Control.Boot.Source + Properties: + BootSource: + Default: Source::Sources::Default + - Interface: xyz.openbmc_project.Control.Boot.Mode + Properties: + BootMode: + Default: Mode::Modes::Regular + +/xyz/openbmc_project/control/host0/boot/one_time: + - Interface: xyz.openbmc_project.Control.Boot.Source + Properties: + BootSource: + Default: Source::Sources::Default + - Interface: xyz.openbmc_project.Control.Boot.Mode + Properties: + BootMode: + Default: Mode::Modes::Regular + - Interface: xyz.openbmc_project.Object.Enable + Properties: + Enabled: + Default: 'true' + +/xyz/openbmc_project/control/host0/power_cap: + - Interface: xyz.openbmc_project.Control.Power.Cap + Properties: + PowerCap: + Default: 0 + Validation: + Type: "range" + Validator: "0..1000" + Unit: "Watts" + PowerCapEnable: + Default: 'false' + +/xyz/openbmc_project/control/host0/power_restore_policy: + - Interface: xyz.openbmc_project.Control.Power.RestorePolicy + Properties: + PowerRestorePolicy: + Default: RestorePolicy::Policy::AlwaysOff + +/xyz/openbmc_project/control/power_restore_delay: + - Interface: xyz.openbmc_project.Control.Power.RestoreDelay + Properties: + PowerRestoreDelay: + Default: 0 + +/xyz/openbmc_project/control/host0/acpi_power_state: + - Interface: xyz.openbmc_project.Control.Power.ACPIPowerState + Properties: + SysACPIStatus: + Default: ACPIPowerState::ACPI::Unknown + DevACPIStatus: + Default: ACPIPowerState::ACPI::Unknown + +/xyz/openbmc_project/time/owner: + - Interface: xyz.openbmc_project.Time.Owner + Properties: + TimeOwner: + Default: Owner::Owners::BMC + +/xyz/openbmc_project/time/sync_method: + - Interface: xyz.openbmc_project.Time.Synchronization + Properties: + TimeSyncMethod: + Default: Synchronization::Method::NTP + +/xyz/openbmc_project/network/host0/intf: + - Interface: xyz.openbmc_project.Network.MACAddress + Properties: + MACAddress: + Default: '"00:00:00:00:00:00"' + Validation: + Type: "regex" + Validator: '^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$' + +#needs to implement address validation TODO openbmc/issues/2046 +/xyz/openbmc_project/network/host0/intf/addr: + - Interface: xyz.openbmc_project.Network.IP + Properties: + Address: + Default: '"0.0.0.0"' + PrefixLength: + Default: 0 + Validation: + Type: "range" + Validator: 0..128 + Unit: "bits" + Origin: + Default: IP::AddressOrigin::Static + Gateway: + Default: '"0.0.0.0"' + Type: + Default: IP::Protocol::IPv4 + +/xyz/openbmc_project/control/host0/restriction_mode: + - Interface: xyz.openbmc_project.Control.Security.RestrictionMode + Properties: + RestrictionMode: + Default: RestrictionMode::Modes::None + +/xyz/openbmc_project/control/host0/TPMEnable: + - Interface: xyz.openbmc_project.Control.TPM.Policy + Properties: + TPMEnable: + Default: 'false' + +/xyz/openbmc_project/control/power_supply_redundancy: + - Interface: xyz.openbmc_project.Control.PowerSupplyRedundancy + Properties: + PowerSupplyRedundancyEnabled: + Default: 'true' + +/xyz/openbmc_project/control/host0/turbo_allowed: + - Interface: xyz.openbmc_project.Control.Host.TurboAllowed + Properties: + TurboAllowed: + Default: 'true' + +/xyz/openbmc_project/control/host0/systemGUID: + - Interface: xyz.openbmc_project.Common.UUID + Properties: + UUID: + Default: '"00000000-0000-0000-0000-000000000000"' + +/xyz/openbmc_project/bios: + - Interface: xyz.openbmc_project.Inventory.Item.Bios + Properties: + BiosId: + Default: '"NA"' + +/xyz/openbmc_project/control/processor_error_config: + - Interface: xyz.openbmc_project.Control.Processor.ErrConfig + Properties: + ResetCfg: + Default: 0 + ResetErrorOccurrenceCounts: + Default: 0 + +/xyz/openbmc_project/control/shutdown_policy_config: + - Interface: xyz.openbmc_project.Control.ShutdownPolicy + Properties: + Policy: + Default: 0 + +/xyz/openbmc_project/control/chassis_capabilities_config: + - Interface: xyz.openbmc_project.Control.ChassisCapabilities + Properties: + CapabilitiesFlags: + Default: 0 + FRUDeviceAddress: + Default: 0x20 + SDRDeviceAddress: + Default: 0x20 + SELDeviceAddress: + Default: 0x20 + SMDeviceAddress: + Default: 0x20 + BridgeDeviceAddress: + Default: 0x20 + +/xyz/openbmc_project/control/thermal_mode: + - Interface: xyz.openbmc_project.Control.ThermalMode + Properties: + Current: + Default: '"Performance"' + Supported: + Default: '{"Acoustic", "Performance"}' diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-manager/0001-settings-initialize-data-file-with-default-setting.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-manager/0001-settings-initialize-data-file-with-default-setting.patch new file mode 100644 index 000000000..fcf2415d6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-manager/0001-settings-initialize-data-file-with-default-setting.patch @@ -0,0 +1,37 @@ +From 9e99aa4f72f4420e03ec2e4a29816eae43c5e748 Mon Sep 17 00:00:00 2001 +From: "Jia, Chunhui" <chunhui.jia@intel.com> +Date: Tue, 29 May 2018 16:16:06 +0800 +Subject: [PATCH] [settings] initialize data file with default setting + +Current code trys to load settings from file at startup. When file +does not exist, it will just use default setting. However, it will +still load default on next reboot because no one create files. + +This change creates file as well when daemon loads default so next +time daemon could load/save from file. + +Signed-off-by: Jia, Chunhui <chunhui.jia@intel.com> +--- + settings_manager.mako.hpp | 3 +++ + 1 file changed, 3 insertions(+) + mode change 100644 => 100755 settings_manager.mako.hpp + +diff --git a/settings_manager.mako.hpp b/settings_manager.mako.hpp +old mode 100644 +new mode 100755 +index 09a5a1f..cd592a0 +--- a/settings_manager.mako.hpp ++++ b/settings_manager.mako.hpp +@@ -323,6 +323,9 @@ class Manager + else + { + initSetting${index}(); ++ std::ofstream ostr(path.c_str(), std::ios::out); ++ cereal::JSONOutputArchive oarchive(ostr); ++ oarchive(*std::get<${index}>(settings)); //create file with default + } + } + catch (cereal::Exception& e) +-- +2.16.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-manager_%.bbappend new file mode 100644 index 000000000..bc695abe8 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/settings/phosphor-settings-manager_%.bbappend @@ -0,0 +1,4 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://0001-settings-initialize-data-file-with-default-setting.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager_git.bb new file mode 100644 index 000000000..184b539a9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager_git.bb @@ -0,0 +1,28 @@ +SUMMARY = "Service configuration manager daemon to control service properties" +DESCRIPTION = "Daemon controls service properies like port, channels, state etc.." + +PV = "1.0+git${SRCPV}" + +S = "${WORKDIR}/git/srvcfg-manager" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SRC_URI = "git://git@github.com/Intel-BMC/provingground.git;protocol=ssh" +SRCREV = "3cc86d6c536b4c5ee7afb5447837b83ce8b3d149" + +inherit cmake systemd +SYSTEMD_SERVICE_${PN} = "srvcfg-manager.service" + +DEPENDS += " \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-logging \ + boost \ + " +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-logging \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0001-Modify-dbus-interface-for-power-control.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0001-Modify-dbus-interface-for-power-control.patch new file mode 100644 index 000000000..fac9b52f1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0001-Modify-dbus-interface-for-power-control.patch @@ -0,0 +1,38 @@ +From d34a2a5f6ca0564275ed0e2664624525cad64585 Mon Sep 17 00:00:00 2001 +From: Yong Li <yong.b.li@linux.intel.com> +Date: Fri, 13 Jul 2018 09:08:52 +0800 +Subject: [PATCH] Modify dbus interface for power control +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Switch power control service namespace from “org” to “xyz”, +to compatible with new intel-chassis services + +Change-Id: I1bf5e218f72eb9fd4fb6f203c35479818d12b1fa +Signed-off-by: Yong Li <yong.b.li@linux.intel.com> +--- + chassis_state_manager.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/chassis_state_manager.cpp b/chassis_state_manager.cpp +index 03dd176..05e2440 100644 +--- a/chassis_state_manager.cpp ++++ b/chassis_state_manager.cpp +@@ -63,10 +63,11 @@ void Chassis::determineInitialState() + { + sdbusplus::message::variant<int> pgood = -1; + auto method = this->bus.new_method_call( +- "org.openbmc.control.Power", "/org/openbmc/control/power0", ++ "xyz.openbmc_project.Chassis.Control.Power", ++ "/xyz/openbmc_project/Chassis/Control/Power0", + "org.freedesktop.DBus.Properties", "Get"); + +- method.append("org.openbmc.control.Power", "pgood"); ++ method.append("xyz.openbmc_project.Chassis.Control.Power", "pgood"); + try + { + auto reply = this->bus.call(method); +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0002-Capture-host-restart-cause.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0002-Capture-host-restart-cause.patch new file mode 100644 index 000000000..7c6f684ca --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/0002-Capture-host-restart-cause.patch @@ -0,0 +1,192 @@ +From 8dea573181c4455e144335e14cac9f54ebbf7208 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Tue, 7 Aug 2018 16:43:00 +0800 +Subject: [PATCH] Capture host restart cause + +Capture host restart cause on power/reset button pressed. +Save the restart cause into file system. +And restort it when BMC boot up. + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + configure.ac | 4 +-- + host_state_manager.cpp | 16 ++++++++++++ + host_state_manager.hpp | 56 +++++++++++++++++++++++++++++++++++++++--- + 3 files changed, 71 insertions(+), 5 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 838aaf2..5879e2f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -54,9 +54,9 @@ AC_ARG_VAR(HOST_RUNNING_FILE, [File to create if host is running]) + AS_IF([test "x$HOST_RUNNING_FILE" == "x"], [HOST_RUNNING_FILE="/run/openbmc/host@%u-on"]) + AC_DEFINE_UNQUOTED([HOST_RUNNING_FILE], ["$HOST_RUNNING_FILE"], [File to create if host is running]) + +-AC_ARG_VAR(HOST_STATE_PERSIST_PATH, [Path of file for storing requested host state.]) ++AC_ARG_VAR(HOST_STATE_PERSIST_PATH, [Path of file for storing host state.]) + AS_IF([test "x$HOST_STATE_PERSIST_PATH" == "x"], \ +- [HOST_STATE_PERSIST_PATH="/var/lib/phosphor-state-manager/requestedHostTransition"]) ++ [HOST_STATE_PERSIST_PATH="/var/lib/phosphor-state-manager/hostState"]) + AC_DEFINE_UNQUOTED([HOST_STATE_PERSIST_PATH], ["$HOST_STATE_PERSIST_PATH"], \ + [Path of file for storing requested host state.]) + +diff --git a/host_state_manager.cpp b/host_state_manager.cpp +index ec1f95f..8573d00 100644 +--- a/host_state_manager.cpp ++++ b/host_state_manager.cpp +@@ -304,6 +304,15 @@ bool Host::deserialize(const fs::path& path) + } + } + ++void Host::restoreHostRestartCause() ++{ ++ if (!deserialize(HOST_STATE_PERSIST_PATH)) ++ { ++ // set to default value ++ server::Host::hostRestartCause(server::Host::RestartCause::Unknown); ++ } ++} ++ + Host::Transition Host::requestedHostTransition(Transition value) + { + log<level::INFO>("Host State transaction request", +@@ -349,6 +358,13 @@ Host::HostState Host::currentHostState(HostState value) + return server::Host::currentHostState(value); + } + ++Host::RestartCause Host::hostRestartCause(RestartCause value) ++{ ++ auto retVal = server::Host::hostRestartCause(value); ++ serialize(); ++ return retVal; ++} ++ + } // namespace manager + } // namespace state + } // namepsace phosphor +diff --git a/host_state_manager.hpp b/host_state_manager.hpp +index 2b00777..e74fab7 100644 +--- a/host_state_manager.hpp ++++ b/host_state_manager.hpp +@@ -32,6 +32,15 @@ using namespace phosphor::logging; + namespace sdbusRule = sdbusplus::bus::match::rules; + namespace fs = std::experimental::filesystem; + ++const static constexpr char* powerButtonPath = ++ "/xyz/openbmc_project/Chassis/Buttons/Power0"; ++const static constexpr char* powerButtonIntf = ++ "xyz.openbmc_project.Chassis.Buttons.Power"; ++const static constexpr char* resetButtonPath = ++ "/xyz/openbmc_project/Chassis/Buttons/Reset0"; ++const static constexpr char* resetButtonIntf = ++ "xyz.openbmc_project.Chassis.Buttons.Reset"; ++ + /** @class Host + * @brief OpenBMC host state management implementation. + * @details A concrete implementation for xyz.openbmc_project.State.Host +@@ -59,7 +68,31 @@ class Host : public HostInherit + sdbusRule::interface("org.freedesktop.systemd1.Manager"), + std::bind(std::mem_fn(&Host::sysStateChange), this, + std::placeholders::_1)), +- settings(bus) ++ settings(bus), ++ powerButtonPressedSignal( ++ bus, ++ sdbusRule::type::signal() + sdbusRule::member("Pressed") + ++ sdbusRule::path(powerButtonPath) + ++ sdbusRule::interface(powerButtonIntf), ++ [this](sdbusplus::message::message &msg) { ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "powerButtonPressedSignal callback function is called..."); ++ this->hostRestartCause(this->RestartCause::PowerButton); ++ return; ++ } ++ ), ++ resetButtonPressedSignal( ++ bus, ++ sdbusRule::type::signal() + sdbusRule::member("Pressed") + ++ sdbusRule::path(resetButtonPath) + ++ sdbusRule::interface(resetButtonIntf), ++ [this](sdbusplus::message::message &msg) { ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "resetButtonPressedSignal callback function is called..."); ++ this->hostRestartCause(this->RestartCause::ResetButton); ++ return; ++ } ++ ) + { + // Enable systemd signals + subscribeToSystemdSignals(); +@@ -69,6 +102,8 @@ class Host : public HostInherit + + attemptsLeft(BOOT_COUNT_MAX_ALLOWED); + ++ restoreHostRestartCause(); // restore host restart cause from persisted file ++ + // We deferred this until we could get our property correct + this->emit_object_added(); + } +@@ -85,6 +120,9 @@ class Host : public HostInherit + /** @brief Set value of CurrentHostState */ + HostState currentHostState(HostState value) override; + ++ /** @brief Set value of HostRestartCause */ ++ RestartCause hostRestartCause(RestartCause value) override; ++ + /** + * @brief Set host reboot count to default + * +@@ -192,7 +230,10 @@ class Host : public HostInherit + server::Progress::bootProgress()), + convertForMessage( + sdbusplus::xyz::openbmc_project::State::OperatingSystem:: +- server::Status::operatingSystemState())); ++ server::Status::operatingSystemState()), ++ convertForMessage(sdbusplus::xyz::openbmc_project::State:: ++ server::Host::hostRestartCause()) ++ ); + } + + /** @brief Function required by Cereal to perform deserialization. +@@ -208,7 +249,8 @@ class Host : public HostInherit + std::string reqTranState; + std::string bootProgress; + std::string osState; +- archive(reqTranState, bootProgress, osState); ++ std::string restartCause; ++ archive(reqTranState, bootProgress, osState, restartCause); + auto reqTran = Host::convertTransitionFromString(reqTranState); + // When restoring, set the requested state with persistent value + // but don't call the override which would execute it +@@ -219,6 +261,8 @@ class Host : public HostInherit + sdbusplus::xyz::openbmc_project::State::OperatingSystem::server:: + Status::operatingSystemState( + Host::convertOSStatusFromString(osState)); ++ sdbusplus::xyz::openbmc_project::State::server::Host:: ++ hostRestartCause(Host::convertRestartCauseFromString(restartCause)); + } + + /** @brief Serialize and persist requested host state +@@ -239,6 +283,9 @@ class Host : public HostInherit + */ + bool deserialize(const fs::path& path); + ++ /** @brief Used to restore HostRestartCause value from persisted file */ ++ void restoreHostRestartCause(); ++ + /** @brief Persistent sdbusplus DBus bus connection. */ + sdbusplus::bus::bus& bus; + +@@ -247,6 +294,9 @@ class Host : public HostInherit + + // Settings objects of interest + settings::Objects settings; ++ ++ sdbusplus::bus::match_t powerButtonPressedSignal; ++ sdbusplus::bus::match_t resetButtonPressedSignal; + }; + + } // namespace manager +-- +2.17.0 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reboot-host@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reboot-host@.service new file mode 100644 index 000000000..ffde01ca3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reboot-host@.service @@ -0,0 +1,18 @@ +[Unit] +Description=Reboot host%i +Wants=obmc-host-stop@%i.target +After=obmc-host-stop@%i.target + +[Service] +#ExecStart={base_bindir}/systemctl start obmc-host-start@%i.target +# This service is starting another target that conflicts with the +# target this service is running in. OpenBMC needs a refactor of +# how it does its host reset path. Until then, this short term +# solution does the job. +# Since this is a part of the reboot target, call the startmin +# target which does the minimum required to start the host. +ExecStart=/bin/sh -c "sleep 10 && systemctl start obmc-host-startmin@%i.target" + + +[Install] +WantedBy=obmc-host-reboot@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reset-host-check@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reset-host-check@.service new file mode 100644 index 000000000..13b8f0fca --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reset-host-check@.service @@ -0,0 +1,19 @@ +[Unit] +Description=Check Host%i status on BMC reset +Wants=mapper-wait@-xyz-openbmc_project-control-host%i.service +After=mapper-wait@-xyz-openbmc_project-control-host%i.service +Wants=obmc-host-reset-running@%i.target +Before=obmc-host-reset-running@%i.target +Wants=op-reset-chassis-on@%i.service +After=op-reset-chassis-on@%i.service +Conflicts=obmc-host-stop@%i.target +ConditionPathExists=/run/openbmc/chassis@%i-on + +[Service] +RemainAfterExit=yes +Type=oneshot +ExecStart=/bin/sh -c "if [ $(busctl get-property `mapper get-service /xyz/openbmc_project/Chassis/Control/Power%i` /xyz/openbmc_project/Chassis/Control/Power%i xyz.openbmc_project.Chassis.Control.Power vrd_good | sed 's/i\s*[1]/on/' | grep on | wc -l) != 0 ]; then mkdir -p /run/openbmc/ && touch /run/openbmc/host@%i-on; fi" +SyslogIdentifier=phosphor-host-check + +[Install] +WantedBy=obmc-host-reset@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reset-host-reboot-attempts@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reset-host-reboot-attempts@.service new file mode 100644 index 000000000..87c750c57 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager/phosphor-reset-host-reboot-attempts@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Reset host reboot counter +Wants=mapper-wait@-xyz-openbmc_project-state-host%i.service +After=mapper-wait@-xyz-openbmc_project-state-host%i.service +ConditionPathExists=!/run/openbmc/host@%i-on + +[Service] +Restart=no +Type=oneshot +ExecStart=/bin/sh -c "busctl set-property `mapper get-service /xyz/openbmc_project/state/host%i` /xyz/openbmc_project/state/host%i xyz.openbmc_project.Control.Boot.RebootAttempts AttemptsLeft u 3" +StartLimitInterval=0 + +[Install] +WantedBy=obmc-host-start@%i.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager_%.bbappend new file mode 100644 index 000000000..92f5e530c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/state/phosphor-state-manager_%.bbappend @@ -0,0 +1,8 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += "file://0001-Modify-dbus-interface-for-power-control.patch \ + file://phosphor-reboot-host@.service \ + file://phosphor-reset-host-reboot-attempts@.service \ + file://phosphor-reset-host-check@.service \ + file://0002-Capture-host-restart-cause.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/system/callback-manager.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/system/callback-manager.bb new file mode 100644 index 000000000..d0fdce645 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/system/callback-manager.bb @@ -0,0 +1,19 @@ +SUMMARY = "Callback Manager" +DESCRIPTION = "D-Bus daemon that registers matches that trigger method calls" + +SRC_URI = "git://git-amr-2.devtools.intel.com:29418/openbmc-provingground;protocol=ssh" + +inherit cmake systemd +DEPENDS = "boost sdbusplus" + +PV = "0.1+git${SRCPV}" +SRCREV = "3cc86d6c536b4c5ee7afb5447837b83ce8b3d149" + +S = "${WORKDIR}/git/callback-manager" + +SYSTEMD_SERVICE_${PN} += "callback-manager.service" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENCE;md5=7becf906c8f8d03c237bad13bc3dac53" + +EXTRA_OECMAKE = "-DYOCTO=1" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/system/obmc-mgr-system%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/system/obmc-mgr-system%.bbappend new file mode 100644 index 000000000..37bb8f961 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/system/obmc-mgr-system%.bbappend @@ -0,0 +1,2 @@ +SYSTEMD_AUTO_ENABLE = "enable" + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager/0005-Added-suport-for-multiple-user-manager-services.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager/0005-Added-suport-for-multiple-user-manager-services.patch new file mode 100644 index 000000000..332933a28 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager/0005-Added-suport-for-multiple-user-manager-services.patch @@ -0,0 +1,1648 @@ +From 4762913cfbd45234ddb363a5ec130eb56a8c7af0 Mon Sep 17 00:00:00 2001 +From: Radivoje Jovanovic <radivoje.jovanovic@intel.com> +Date: Mon, 2 Jul 2018 19:23:25 -0700 +Subject: [PATCH] Added suport for multiple user manager services + +Support added for SSSD service implementation + +Signed-off-by: Alberto Salazar Perez <alberto.salazar.perez@intel.com> +Signed-off-by: Radivoje Jovanovic <radivoje.jovanovic@intel.com> +Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com> +--- + Makefile.am | 5 +- + mainapp.cpp | 89 ++++++- + user_mgr.cpp | 295 +++------------------ + user_mgr.hpp | 9 +- + user_service.cpp | 781 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + user_service.hpp | 233 +++++++++++++++++ + 6 files changed, 1141 insertions(+), 271 deletions(-) + create mode 100644 user_service.cpp + create mode 100644 user_service.hpp + +diff --git a/Makefile.am b/Makefile.am +index 4413b84..e4310d4 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1,12 +1,13 @@ + sbin_PROGRAMS = phosphor-user-manager + +-noinst_HEADERS = user.hpp user_mgr.hpp users.hpp ++noinst_HEADERS = user.hpp user_mgr.hpp users.hpp user_service.hpp + + phosphor_user_manager_SOURCES = \ + user.cpp \ + mainapp.cpp \ + user_mgr.cpp \ +- users.cpp ++ users.cpp \ ++ user_service.cpp + + phosphor_user_manager_LDFLAGS = $(SDBUSPLUS_LIBS) \ + $(PHOSPHOR_DBUS_INTERFACES_LIBS) \ +diff --git a/mainapp.cpp b/mainapp.cpp +index c9da030..03c406a 100644 +--- a/mainapp.cpp ++++ b/mainapp.cpp +@@ -14,18 +14,105 @@ + * limitations under the License. + */ + #include <string> ++#include <iostream> ++#include <getopt.h> + #include "user_mgr.hpp" ++#include "user_service.hpp" + #include "config.h" + + // D-Bus root for user manager + constexpr auto USER_MANAGER_ROOT = "/xyz/openbmc_project/user"; + ++void printUsage() ++{ ++ std::string usage = ++ R"(Usage: ++ phosphor-user-manager [OPTIONS] ++ ++Backend DBUS service for OpenBMC User Management. ++If no OPTIONS are specified, shadow file will be used. ++ ++Options: ++ -s, --service={shadow|sssd} ++ Specify the authentication service to use: ++ 'shadow' will use the /etc/shadow file. ++ 'sssd' will use the sssd service domains. ++ -h, --help Displays this help message. ++)"; ++ std::cerr << usage; ++} ++ ++void parseArgs(int argc, char** argv, ++ phosphor::user::UserService::ServiceType& srvc) ++{ ++ const std::string shortOpts{"s:h"}; ++ const struct option longOpts[] = {{"service", 1, nullptr, 's'}, ++ {"help", 0, nullptr, 'h'}, ++ {nullptr, 0, nullptr, 0}}; ++ ++ while (true) ++ { ++ const auto opt = ++ getopt_long(argc, argv, shortOpts.c_str(), longOpts, nullptr); ++ ++ if (opt == -1) ++ { ++ if (srvc == phosphor::user::UserService::ServiceType::none) ++ { ++ srvc = phosphor::user::UserService::ServiceType::shadow; ++ } ++ break; ++ } ++ ++ switch (opt) ++ { ++ case 's': ++ { ++ std::string srvcStr{optarg}; ++ if (!srvcStr.compare("shadow")) ++ { ++ srvc = phosphor::user::UserService::ServiceType::shadow; ++ } ++ else if (!srvcStr.compare("sssd")) ++ { ++ srvc = phosphor::user::UserService::ServiceType::sssd; ++ } ++ else ++ { ++ std::cerr << "Error. '" << srvcStr << "' is not a valid" ++ << " authentication service." << std::endl; ++ printUsage(); ++ exit(1); ++ } ++ } ++ break; ++ ++ case 'h': ++ { ++ printUsage(); ++ exit(0); ++ } ++ ++ default: ++ { ++ printUsage(); ++ exit(1); ++ } ++ } ++ } ++} ++ + int main(int argc, char** argv) + { ++ // Check command line options. Exit if error. ++ phosphor::user::UserService::ServiceType srvc = ++ phosphor::user::UserService::ServiceType::none; ++ parseArgs(argc, argv, srvc); ++ + auto bus = sdbusplus::bus::new_default(); + sdbusplus::server::manager::manager objManager(bus, USER_MANAGER_ROOT); + +- phosphor::user::UserMgr userMgr(bus, USER_MANAGER_ROOT); ++ phosphor::user::UserMgr userMgr(bus, USER_MANAGER_ROOT, srvc); + + // Claim the bus now + bus.request_name(USER_MANAGER_BUSNAME); +diff --git a/user_mgr.cpp b/user_mgr.cpp +index 786a8fd..51193cc 100644 +--- a/user_mgr.cpp ++++ b/user_mgr.cpp +@@ -14,26 +14,18 @@ + // limitations under the License. + */ + +-#include <shadow.h> +-#include <unistd.h> +-#include <sys/types.h> +-#include <sys/wait.h> ++#include <cstdio> ++ + #include <fstream> +-#include <grp.h> +-#include <pwd.h> + #include <regex> +-#include <algorithm> +-#include <numeric> +-#include <boost/process/child.hpp> +-#include <boost/process/io.hpp> + #include <boost/algorithm/string/split.hpp> + #include <xyz/openbmc_project/Common/error.hpp> + #include <xyz/openbmc_project/User/Common/error.hpp> + #include <phosphor-logging/log.hpp> + #include <phosphor-logging/elog.hpp> + #include <phosphor-logging/elog-errors.hpp> ++#include <stdexcept> + #include "shadowlock.hpp" +-#include "file.hpp" + #include "user_mgr.hpp" + #include "users.hpp" + #include "config.h" +@@ -43,12 +35,10 @@ namespace phosphor + namespace user + { + +-static constexpr const char *passwdFileName = "/etc/passwd"; + static constexpr size_t ipmiMaxUsers = 15; + static constexpr size_t ipmiMaxUserNameLen = 16; + static constexpr size_t systemMaxUserNameLen = 30; + static constexpr size_t maxSystemUsers = 30; +-static constexpr const char *grpSsh = "ssh"; + static constexpr uint8_t minPasswdLength = 8; + static constexpr int success = 0; + static constexpr int failure = -1; +@@ -83,79 +73,6 @@ using NoResource = + + using Argument = xyz::openbmc_project::Common::InvalidArgument; + +-template <typename... ArgTypes> +-static std::vector<std::string> executeCmd(const char *path, +- ArgTypes &&... tArgs) +-{ +- std::vector<std::string> stdOutput; +- boost::process::ipstream stdOutStream; +- boost::process::child execProg(path, const_cast<char *>(tArgs)..., +- boost::process::std_out > stdOutStream); +- std::string stdOutLine; +- +- while (stdOutStream && std::getline(stdOutStream, stdOutLine) && +- !stdOutLine.empty()) +- { +- stdOutput.emplace_back(stdOutLine); +- } +- +- execProg.wait(); +- +- int retCode = execProg.exit_code(); +- if (retCode) +- { +- log<level::ERR>("Command execution failed", entry("PATH=%d", path), +- entry("RETURN_CODE:%d", retCode)); +- elog<InternalFailure>(); +- } +- +- return stdOutput; +-} +- +-static std::string getCSVFromVector(std::vector<std::string> vec) +-{ +- switch (vec.size()) +- { +- case 0: +- { +- return ""; +- } +- break; +- +- case 1: +- { +- return std::string{vec[0]}; +- } +- break; +- +- default: +- { +- return std::accumulate( +- std::next(vec.begin()), vec.end(), vec[0], +- [](std::string a, std::string b) { return a + ',' + b; }); +- } +- } +-} +- +-static bool removeStringFromCSV(std::string &csvStr, const std::string &delStr) +-{ +- std::string::size_type delStrPos = csvStr.find(delStr); +- if (delStrPos != std::string::npos) +- { +- // need to also delete the comma char +- if (delStrPos == 0) +- { +- csvStr.erase(delStrPos, delStr.size() + 1); +- } +- else +- { +- csvStr.erase(delStrPos - 1, delStr.size() + 1); +- } +- return true; +- } +- return false; +-} +- + bool UserMgr::isUserExist(const std::string &userName) + { + if (userName.empty()) +@@ -282,39 +199,14 @@ void UserMgr::createUser(std::string userName, + { + throwForInvalidPrivilege(priv); + throwForInvalidGroups(groupNames); +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); + throwForUserExists(userName); + throwForUserNameConstraints(userName, groupNames); + throwForMaxGrpUserCount(groupNames); + +- std::string groups = getCSVFromVector(groupNames); +- bool sshRequested = removeStringFromCSV(groups, grpSsh); ++ // Tell the User Service to create a new user with the info provided. ++ userSrvc->createUser(userName, groupNames, priv, enabled); + +- // treat privilege as a group - This is to avoid using different file to +- // store the same. +- if (!priv.empty()) +- { +- if (groups.size() != 0) +- { +- groups += ","; +- } +- groups += priv; +- } +- try +- { +- executeCmd("/usr/sbin/useradd", userName.c_str(), "-G", groups.c_str(), +- "-m", "-N", "-s", +- (sshRequested ? "/bin/sh" : "/bin/nologin"), "-e", +- (enabled ? "" : "1970-01-02")); +- } +- catch (const InternalFailure &e) +- { +- log<level::ERR>("Unable to create new user"); +- elog<InternalFailure>(); +- } +- +- // Add the users object before sending out the signal ++ // Add the users to the local list before sending out the signal + std::string userObj = std::string(usersObjPath) + "/" + userName; + std::sort(groupNames.begin(), groupNames.end()); + usersList.emplace( +@@ -328,19 +220,11 @@ void UserMgr::createUser(std::string userName, + + void UserMgr::deleteUser(std::string userName) + { +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); + throwForUserDoesNotExist(userName); +- try +- { +- executeCmd("/usr/sbin/userdel", userName.c_str(), "-r"); +- } +- catch (const InternalFailure &e) +- { +- log<level::ERR>("User delete failed", +- entry("USER_NAME=%s", userName.c_str())); +- elog<InternalFailure>(); +- } ++ ++ // Tell the User Service to delete user ++ userSrvc->deleteUser(userName); ++ // Then delete user from local list + + usersList.erase(userName); + +@@ -351,24 +235,13 @@ void UserMgr::deleteUser(std::string userName) + + void UserMgr::renameUser(std::string userName, std::string newUserName) + { +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); + throwForUserDoesNotExist(userName); + throwForUserExists(newUserName); + throwForUserNameConstraints(newUserName, + usersList[userName].get()->userGroups()); +- try +- { +- std::string newHomeDir = "/home/" + newUserName; +- executeCmd("/usr/sbin/usermod", "-l", newUserName.c_str(), +- userName.c_str(), "-d", newHomeDir.c_str(), "-m"); +- } +- catch (const InternalFailure &e) +- { +- log<level::ERR>("User rename failed", +- entry("USER_NAME=%s", userName.c_str())); +- elog<InternalFailure>(); +- } ++ // Call The User Service to rename user on the system ++ userSrvc->renameUser(userName, newUserName); ++ // Update local list to reflect the name change + const auto &user = usersList[userName]; + std::string priv = user.get()->userPrivilege(); + std::vector<std::string> groupNames = user.get()->userGroups(); +@@ -392,8 +265,6 @@ void UserMgr::updateGroupsAndPriv(const std::string &userName, + { + throwForInvalidPrivilege(priv); + throwForInvalidGroups(groupNames); +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); + throwForUserDoesNotExist(userName); + const std::vector<std::string> &oldGroupNames = + usersList[userName].get()->userGroups(); +@@ -409,29 +280,8 @@ void UserMgr::updateGroupsAndPriv(const std::string &userName, + throwForMaxGrpUserCount(groupNames); + } + +- std::string groups = getCSVFromVector(groupNames); +- bool sshRequested = removeStringFromCSV(groups, grpSsh); +- +- // treat privilege as a group - This is to avoid using different file to +- // store the same. +- if (!priv.empty()) +- { +- if (groups.size() != 0) +- { +- groups += ","; +- } +- groups += priv; +- } +- try +- { +- executeCmd("/usr/sbin/usermod", userName.c_str(), "-G", groups.c_str(), +- "-s", (sshRequested ? "/bin/sh" : "/bin/nologin")); +- } +- catch (const InternalFailure &e) +- { +- log<level::ERR>("Unable to modify user privilege / groups"); +- elog<InternalFailure>(); +- } ++ // Call The User Service to update user groups and priv on the system ++ userSrvc->updateGroupsAndPriv(userName, groupNames, priv); + + log<level::INFO>("User groups / privilege updated successfully", + entry("USER_NAME=%s", userName.c_str())); +@@ -627,19 +477,9 @@ int UserMgr::setPamModuleArgValue(const std::string &moduleName, + + void UserMgr::userEnable(const std::string &userName, bool enabled) + { +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); + throwForUserDoesNotExist(userName); +- try +- { +- executeCmd("/usr/sbin/usermod", userName.c_str(), "-e", +- (enabled ? "" : "1970-01-02")); +- } +- catch (const InternalFailure &e) +- { +- log<level::ERR>("Unable to modify user enabled state"); +- elog<InternalFailure>(); +- } ++ // Call The User Service to update user groups and priv on the system ++ userSrvc->updateUserStatus(userName, enabled); + + log<level::INFO>("User enabled/disabled state updated successfully", + entry("USER_NAME=%s", userName.c_str()), +@@ -730,49 +570,8 @@ bool UserMgr::userLockedForFailedAttempt(const std::string &userName, + + UserSSHLists UserMgr::getUserAndSshGrpList() + { +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); +- +- std::vector<std::string> userList; +- std::vector<std::string> sshUsersList; +- struct passwd pw, *pwp = nullptr; +- std::array<char, 1024> buffer{}; +- +- phosphor::user::File passwd(passwdFileName, "r"); +- if ((passwd)() == NULL) +- { +- log<level::ERR>("Error opening the passwd file"); +- elog<InternalFailure>(); +- } +- +- while (true) +- { +- auto r = fgetpwent_r((passwd)(), &pw, buffer.data(), buffer.max_size(), +- &pwp); +- if ((r != 0) || (pwp == NULL)) +- { +- // Any error, break the loop. +- break; +- } +- // Add all users whose UID >= 1000 and < 65534 +- // and special UID 0. +- if ((pwp->pw_uid == 0) || +- ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534))) +- { +- std::string userName(pwp->pw_name); +- userList.emplace_back(userName); +- +- // ssh doesn't have separate group. Check login shell entry to +- // get all users list which are member of ssh group. +- std::string loginShell(pwp->pw_shell); +- if (loginShell == "/bin/sh") +- { +- sshUsersList.emplace_back(userName); +- } +- } +- } +- endpwent(); +- return std::make_pair(std::move(userList), std::move(sshUsersList)); ++ // Call The User Service to get the User and SSUsers lists ++ return std::move(userSrvc->getUserAndSshGrpList()); + } + + size_t UserMgr::getIpmiUsersCount() +@@ -783,60 +582,23 @@ size_t UserMgr::getIpmiUsersCount() + + bool UserMgr::isUserEnabled(const std::string &userName) + { +- // All user management lock has to be based on /etc/shadow +- phosphor::user::shadow::Lock lock(); +- std::array<char, 4096> buffer{}; +- struct spwd spwd; +- struct spwd *resultPtr = nullptr; +- int status = getspnam_r(userName.c_str(), &spwd, buffer.data(), +- buffer.max_size(), &resultPtr); +- if (!status && (&spwd == resultPtr)) +- { +- if (resultPtr->sp_expire >= 0) +- { +- return false; // user locked out +- } +- return true; +- } +- return false; // assume user is disabled for any error. ++ // Call The User Service to verify if user is enabled ++ return userSrvc->isUserEnabled(userName); + } + + std::vector<std::string> UserMgr::getUsersInGroup(const std::string &groupName) + { +- std::vector<std::string> usersInGroup; +- // Should be more than enough to get the pwd structure. +- std::array<char, 4096> buffer{}; +- struct group grp; +- struct group *resultPtr = nullptr; +- +- int status = getgrnam_r(groupName.c_str(), &grp, buffer.data(), +- buffer.max_size(), &resultPtr); +- +- if (!status && (&grp == resultPtr)) +- { +- for (; *(grp.gr_mem) != NULL; ++(grp.gr_mem)) +- { +- usersInGroup.emplace_back(*(grp.gr_mem)); +- } +- } +- else +- { +- log<level::ERR>("Group not found", +- entry("GROUP=%s", groupName.c_str())); +- // Don't throw error, just return empty userList - fallback +- } +- return usersInGroup; ++ // Call The User Service to get the users that belong to a group ++ return std::move(userSrvc->getUsersInGroup(groupName)); + } + + void UserMgr::initUserObjects(void) + { + // All user management lock has to be based on /etc/shadow + phosphor::user::shadow::Lock lock(); +- std::vector<std::string> userNameList; +- std::vector<std::string> sshGrpUsersList; + UserSSHLists userSSHLists = getUserAndSshGrpList(); +- userNameList = std::move(userSSHLists.first); +- sshGrpUsersList = std::move(userSSHLists.second); ++ std::vector<std::string> userNameList = std::move(userSSHLists.first); ++ std::vector<std::string> sshGrpUsersList = std::move(userSSHLists.second); + + if (!userNameList.empty()) + { +@@ -891,8 +653,10 @@ void UserMgr::initUserObjects(void) + } + } + +-UserMgr::UserMgr(sdbusplus::bus::bus &bus, const char *path) : +- UserMgrIface(bus, path), AccountPolicyIface(bus, path), bus(bus), path(path) ++UserMgr::UserMgr(sdbusplus::bus::bus &bus, const char *path, ++ UserService::ServiceType srvc) : ++ UserMgrIface(bus, path), ++ AccountPolicyIface(bus, path), bus(bus), path(path) + { + UserMgrIface::allPrivileges(privMgr); + std::sort(groupsMgr.begin(), groupsMgr.end()); +@@ -1000,6 +764,7 @@ UserMgr::UserMgr(sdbusplus::bus::bus &bus, const char *path) : + } + AccountPolicyIface::accountUnlockTimeout(value32); + } ++ userSrvc = std::make_unique<UserService>(srvc, groupsMgr, privMgr); + initUserObjects(); + } + +diff --git a/user_mgr.hpp b/user_mgr.hpp +index c1673f1..169f121 100644 +--- a/user_mgr.hpp ++++ b/user_mgr.hpp +@@ -20,6 +20,7 @@ + #include <xyz/openbmc_project/User/AccountPolicy/server.hpp> + #include <unordered_map> + #include "users.hpp" ++#include "user_service.hpp" + + namespace phosphor + { +@@ -27,8 +28,6 @@ namespace user + { + + using UserMgrIface = sdbusplus::xyz::openbmc_project::User::server::Manager; +-using UserSSHLists = +- std::pair<std::vector<std::string>, std::vector<std::string>>; + using AccountPolicyIface = + sdbusplus::xyz::openbmc_project::User::server::AccountPolicy; + +@@ -49,8 +48,10 @@ class UserMgr : public UserMgrIface, AccountPolicyIface + * + * @param[in] bus - sdbusplus handler + * @param[in] path - D-Bus path ++ * @param[in] srvc - User service to be used + */ +- UserMgr(sdbusplus::bus::bus &bus, const char *path); ++ UserMgr(sdbusplus::bus::bus &bus, const char *path, ++ UserService::ServiceType srvc); + + /** @brief create user method. + * This method creates a new user as requested +@@ -148,6 +149,8 @@ class UserMgr : public UserMgrIface, AccountPolicyIface + /** @brief object path */ + const std::string path; + ++ /** @brief user service to be used */ ++ std::unique_ptr<UserService> userSrvc; + /** @brief privilege manager container */ + std::vector<std::string> privMgr = {"priv-admin", "priv-operator", + "priv-user", "priv-callback"}; +diff --git a/user_service.cpp b/user_service.cpp +new file mode 100644 +index 0000000..9bb602c +--- /dev/null ++++ b/user_service.cpp +@@ -0,0 +1,781 @@ ++/* ++// Copyright (c) 2018 Intel Corporation ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++*/ ++ ++#include <grp.h> ++#include <pwd.h> ++#include <numeric> ++#include <boost/process/child.hpp> ++#include <boost/process/io.hpp> ++#include <boost/algorithm/string/split.hpp> ++#include "shadowlock.hpp" ++#include "file.hpp" ++#include "user_service.hpp" ++ ++/* anonymous namespace for User Service interface implementations. ++// Each class inside this namespace implements a special service ++// to be used for the User Manager class. This can be extended to use ++// other user management services and it should be as simple as ++// adding a new class which inherits from phosphor::user::UserServiceInterface ++*/ ++ ++namespace ++{ ++ ++std::string getCSVFromVector(std::vector<std::string> vec) ++{ ++ switch (vec.size()) ++ { ++ case 0: ++ { ++ return ""; ++ } ++ break; ++ ++ case 1: ++ { ++ return std::string{vec[0]}; ++ } ++ break; ++ ++ default: ++ { ++ return std::accumulate( ++ std::next(vec.begin()), vec.end(), vec[0], ++ [](std::string a, std::string b) { return a + ',' + b; }); ++ } ++ } ++} ++ ++bool removeStringFromCSV(std::string &csvStr, const std::string &delStr) ++{ ++ std::string::size_type delStrPos = csvStr.find(delStr); ++ if (delStrPos != std::string::npos) ++ { ++ // need to also delete the comma char ++ if (delStrPos == 0) ++ { ++ csvStr.erase(delStrPos, delStr.size() + 1); ++ } ++ else ++ { ++ csvStr.erase(delStrPos - 1, delStr.size() + 1); ++ } ++ return true; ++ } ++ return false; ++} ++ ++class ShadowService : public phosphor::user::UserServiceInterface ++{ ++ public: ++ ShadowService() = default; ++ ++ ~ShadowService() = default; ++ ++ phosphor::user::UserSSHLists getUserAndSshGrpList() const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ ++ std::vector<std::string> userList; ++ std::vector<std::string> sshUsersList; ++ ++ struct passwd pw, *pwp = nullptr; ++ std::array<char, 1024> buffer{}; ++ ++ phosphor::user::File passwd(passwdFileName, "r"); ++ if ((passwd)() == NULL) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Error opening the passwd file"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ while (true) ++ { ++ auto r = fgetpwent_r((passwd)(), &pw, buffer.data(), ++ buffer.max_size(), &pwp); ++ if ((r != 0) || (pwp == NULL)) ++ { ++ // Any error, break the loop. ++ break; ++ } ++ // Add all users whose UID >= 1000 and < 65534 ++ // and special UID 0. ++ if ((pwp->pw_uid == 0) || ++ ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534))) ++ { ++ std::string userName(pwp->pw_name); ++ userList.emplace_back(userName); ++ ++ // ssh doesn't have separate group. Check login shell entry to ++ // get all users list which are member of ssh group. ++ std::string loginShell(pwp->pw_shell); ++ if (loginShell == "/bin/sh") ++ { ++ sshUsersList.emplace_back(userName); ++ } ++ } ++ } ++ endpwent(); ++ return std::make_pair(std::move(userList), std::move(sshUsersList)); ++ } ++ ++ std::vector<std::string> ++ getUsersInGroup(const std::string &groupName) const override ++ { ++ std::vector<std::string> usersInGroup; ++ // Should be more than enough to get the pwd structure. ++ std::array<char, 4096> buffer{}; ++ struct group grp; ++ struct group *grpPtr = &grp; ++ struct group *resultPtr; ++ ++ int status = getgrnam_r(groupName.c_str(), grpPtr, buffer.data(), ++ buffer.max_size(), &resultPtr); ++ ++ if (!status && (grpPtr == resultPtr)) ++ { ++ for (; *(grp.gr_mem) != NULL; ++(grp.gr_mem)) ++ { ++ usersInGroup.emplace_back(*(grp.gr_mem)); ++ } ++ } ++ else ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Group not found", ++ phosphor::logging::entry("GROUP=%s", groupName.c_str())); ++ // Don't throw error, just return empty usersInGroup - fallback ++ } ++ return usersInGroup; ++ } ++ ++ void createUser(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv, const bool &enabled) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ ++ std::string groups = getCSVFromVector(groupNames); ++ bool sshRequested = removeStringFromCSV(groups, phosphor::user::grpSsh); ++ ++ // treat privilege as a group - This is to avoid using different file to ++ // store the same ++ if (!priv.empty()) ++ { ++ if (groups.size() != 0) ++ { ++ groups.append(","); ++ } ++ groups.append(priv); ++ } ++ ++ try ++ { ++ phosphor::user::executeCmd( ++ "/usr/sbin/useradd", userName.c_str(), "-G", groups.c_str(), ++ "-m", "-N", "-s", (sshRequested ? "/bin/sh" : "/bin/nologin"), ++ "-e", (enabled ? "" : "1970-01-02")); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to create new user"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void renameUser(const std::string &userName, ++ const std::string &newUserName) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ try ++ { ++ std::string newHomeDir = "/home/" + newUserName; ++ phosphor::user::executeCmd("/usr/sbin/usermod", "-l", ++ newUserName.c_str(), userName.c_str(), ++ "-d", newHomeDir.c_str(), "-m"); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "User rename failed", ++ phosphor::logging::entry("USER_NAME=%s", userName.c_str())); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void deleteUser(const std::string &userName) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ ++ try ++ { ++ phosphor::user::executeCmd("/usr/sbin/userdel", userName.c_str(), ++ "-r"); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::INFO>( ++ "User delete failed", ++ phosphor::logging::entry("USER_NAME=%s", userName.c_str())); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void updateGroupsAndPriv(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ ++ std::string groups = getCSVFromVector(groupNames); ++ bool sshRequested = removeStringFromCSV(groups, phosphor::user::grpSsh); ++ ++ // treat privilege as a group - This is to avoid using different file to ++ // store the same. ++ if (!priv.empty()) ++ { ++ if (groups.size() != 0) ++ { ++ groups += ","; ++ } ++ groups += priv; ++ } ++ ++ try ++ { ++ phosphor::user::executeCmd( ++ "/usr/sbin/usermod", userName.c_str(), "-G", groups.c_str(), ++ "-s", (sshRequested ? "/bin/sh" : "/bin/nologin")); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to modify user privilege / groups"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void updateUserStatus(const std::string &userName, ++ const bool &enabled) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ try ++ { ++ phosphor::user::executeCmd("/usr/sbin/usermod", userName.c_str(), ++ "-e", (enabled ? "" : "1970-01-02")); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to modify user enabled state"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ bool isUserEnabled(const std::string &userName) const override ++ { ++ // All user management lock has to be based on /etc/shadow ++ phosphor::user::shadow::Lock lock(); ++ std::array<char, 4096> buffer{}; ++ struct spwd spwd; ++ struct spwd *resultPtr = nullptr; ++ int status = getspnam_r(userName.c_str(), &spwd, buffer.data(), ++ buffer.max_size(), &resultPtr); ++ if (!status && (&spwd == resultPtr)) ++ { ++ if (resultPtr->sp_expire >= 0) ++ { ++ return false; // user locked out ++ } ++ return true; ++ } ++ return false; // assume user is disabled for any error. ++ } ++ ++ std::vector<std::string> ++ getUserGroups(const std::string &userName) const override ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "ShadowService::getUserGroups not implemented!"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ return std::vector<std::string>(); ++ } ++ ++ void createGroup(const std::string &groupName) const override ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "ShadowService::createGroup not implemented!"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ private: ++ static constexpr const char *passwdFileName = "/etc/passwd"; ++}; ++ ++class SSSDService : public phosphor::user::UserServiceInterface ++{ ++ public: ++ SSSDService(const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs) ++ { ++ ++ createGroup(lockedGrp); ++ for (const auto &g : groups) ++ { ++ createGroup(g); ++ } ++ for (const auto &p : privs) ++ { ++ createGroup(p); ++ } ++ } ++ ++ ~SSSDService() = default; ++ ++ phosphor::user::UserSSHLists getUserAndSshGrpList() const override ++ { ++ std::vector<std::string> users; ++ std::vector<std::string> sshGroup; ++ std::vector<std::string> exeOutput; ++ ++ try ++ { ++ exeOutput = phosphor::user::executeCmd("/usr/bin/getent", "-s", ++ "sss", "passwd"); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get users information " ++ "from sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ for (const auto &userLine : exeOutput) ++ { ++ std::vector<std::string> userInfo; ++ boost::algorithm::split(userInfo, userLine, ++ boost::algorithm::is_any_of(":")); ++ // At this point userInfo is a vector containing the passwd ++ // info for the user, so we know the correct positions: ++ // 0: User name. ++ // 1: Encrypted password. ++ // 2: User ID number (UID) ++ // 3: User's group ID number (GID) ++ // 4: Full name of the user (GECOS) ++ // 5: User home directory. ++ // 6: Login shell. ++ users.emplace_back(userInfo[0]); ++ ++ // ssh doesn't have separate group. Check login shell entry to ++ // get all users list which are member of ssh group. ++ if (userInfo[6] == "/bin/sh") ++ { ++ sshGroup.emplace_back(userInfo[0]); ++ } ++ } ++ ++ return std::make_pair(std::move(users), std::move(sshGroup)); ++ } ++ ++ std::vector<std::string> ++ getUsersInGroup(const std::string &groupName) const override ++ { ++ std::vector<std::string> userList; ++ std::vector<std::string> exeOutput; ++ ++ try ++ { ++ exeOutput = phosphor::user::executeCmd("/usr/sbin/sss_groupshow", ++ groupName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get group users from sssd service"); ++ // Don't throw error, just return empty usersInGroup - return ++ return userList; ++ } ++ // exeOutput should have 5 entries ++ // 0: Group ++ // 1: GID number ++ // 2: Member users ++ // 3: Is a member of ++ // 4: Member groups ++ exeOutput[2].erase( ++ exeOutput[2].begin(), ++ std::find(exeOutput[2].begin(), exeOutput[2].end(), ':')); ++ boost::algorithm::trim_left(exeOutput[2]); ++ boost::algorithm::split(userList, exeOutput[2], ++ boost::algorithm::is_any_of(",")); ++ return userList; ++ } ++ ++ void createUser(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv, const bool &enabled) const override ++ { ++ std::string groups = getCSVFromVector(groupNames); ++ bool sshRequested = removeStringFromCSV(groups, phosphor::user::grpSsh); ++ // treat privilege as a group - This is to avoid using different file to ++ // store the same ++ if (!priv.empty()) ++ { ++ if (groups.size() != 0) ++ { ++ groups += ","; ++ } ++ groups += priv; ++ } ++ ++ try ++ { ++ phosphor::user::executeCmd( ++ "/usr/sbin/sss_useradd", "-m", "-G", groups.c_str(), "-s", ++ (sshRequested ? "/bin/sh" : "/bin/nologin"), userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to create new user in sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ // Sometimes the SSSD service needs some time to actually ++ // reflect the changes to the local DB to the NSS service, ++ // that is why we have this sleep here ... ++ std::this_thread::sleep_for(std::chrono::seconds(1)); ++ // update user status (locked/unlocked) ++ updateUserStatus(userName, enabled); ++ } ++ ++ void renameUser(const std::string &userName, ++ const std::string &newUserName) const override ++ { ++ std::vector<std::string> exeOutput; ++ // Local Domain for sssd doesn't have a rename feature ++ // so we need to first create a new user and then delete ++ // the old one. ++ // The only issue with this is that the password for the ++ // user will have to be reseted since it is a new user being created. ++ ++ // Get original user groups ++ std::vector<std::string> groups = getUserGroups(userName); ++ // Check if it has a "ssh" group by looking for the shell login ++ try ++ { ++ exeOutput = phosphor::user::executeCmd( ++ "/usr/bin/getent", "-s", "sss", "passwd", userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get information for user"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ if (exeOutput[0].find("/bin/sh")) ++ { ++ groups.emplace_back(phosphor::user::grpSsh); ++ } ++ // Call create user with the new user names and previous groups ++ // Priv is already part of the groups so that can be empty. ++ createUser(newUserName, groups, "", isUserEnabled(userName)); ++ ++ // Now delete original user ++ deleteUser(userName); ++ } ++ ++ void deleteUser(const std::string &userName) const override ++ { ++ try ++ { ++ phosphor::user::executeCmd("/usr/sbin/sss_userdel", "-r", ++ userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to delete user from sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void updateGroupsAndPriv(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv) const override ++ { ++ // local domain sssd do not allow to update all list of groups, ++ // so we will remove all groups first (except for the user one) ++ // and then all all the ones that were passed ++ std::string oldGroups = getCSVFromVector(getUserGroups(userName)); ++ std::string groups = getCSVFromVector(groupNames); ++ bool sshRequested = removeStringFromCSV(groups, phosphor::user::grpSsh); ++ // treat privilege as a group - This is to avoid using different file to ++ // store the same ++ if (!priv.empty()) ++ { ++ if (groups.size() != 0) ++ { ++ groups += ","; ++ } ++ groups += priv; ++ } ++ try ++ { ++ phosphor::user::executeCmd( ++ "/usr/sbin/sss_usermod", "-r", oldGroups.c_str(), "-a", ++ groups.c_str(), "-s", ++ (sshRequested ? "/bin/sh" : "/bin/nologin"), userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to update user groups and " ++ "priv from sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ void updateUserStatus(const std::string &userName, ++ const bool &enabled) const override ++ { ++ std::string enabledStr; ++ std::string lockedStr; ++ if (isUserEnabled(userName) == enabled) ++ { ++ return; ++ } ++ if (enabled) ++ { ++ enabledStr = "-r"; ++ lockedStr = "-U"; ++ } ++ else ++ { ++ enabledStr = "-a"; ++ lockedStr = "-L"; ++ } ++ try ++ { ++ // We will add a special locked group to identify the users ++ // that have been locked out of the system. ++ // TODO: sss_usermod is not locking user accounts for the ++ // LOCAL domain, need to find the correct PAM configuration ++ // to actually lockout users for SSSD. ++ // As a workaround we are using the pam module pam_listfile.so ++ // to lockout all users that belong to the locked group. ++ phosphor::user::executeCmd("/usr/sbin/sss_usermod", ++ enabledStr.c_str(), lockedGrp.c_str(), ++ lockedStr.c_str(), userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to update user status from sssd service"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ bool isUserEnabled(const std::string &userName) const override ++ { ++ std::vector<std::string> userGrps = getUserGroups(userName); ++ return std::find(userGrps.begin(), userGrps.end(), lockedGrp) == ++ userGrps.end(); ++ } ++ ++ std::vector<std::string> ++ getUserGroups(const std::string &userName) const override ++ { ++ std::vector<std::string> exeOutput; ++ try ++ { ++ exeOutput = ++ phosphor::user::executeCmd("/usr/bin/groups", userName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to get groups for user"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ std::vector<std::string> groups; ++ boost::algorithm::split(groups, exeOutput[0], ++ boost::algorithm::is_any_of(" ")); ++ // Delete group that equals user name if it exists ++ auto userNameGroup = std::find(groups.begin(), groups.end(), userName); ++ if (userNameGroup != groups.end()) ++ { ++ groups.erase(userNameGroup); ++ } ++ return groups; ++ } ++ ++ void createGroup(const std::string &groupName) const override ++ { ++ try ++ { ++ if (!groupExists(groupName)) ++ { ++ phosphor::user::executeCmd("/usr/sbin/sss_groupadd", ++ groupName.c_str()); ++ } ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Unable to create group"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ } ++ ++ private: ++ static const std::string lockedGrp; ++ ++ bool groupExists(const std::string &groupName) const ++ { ++ try ++ { ++ phosphor::user::executeCmd("/usr/sbin/sss_groupshow", ++ groupName.c_str()); ++ } ++ catch (const phosphor::user::InternalFailure &e) ++ { ++ return false; ++ } ++ return true; ++ } ++}; ++ ++const std::string SSSDService::lockedGrp = "sssd_locked"; ++} // anonymous namespace ++ ++namespace phosphor ++{ ++namespace user ++{ ++ ++UserService::UserService(const ServiceType &srvcType, ++ const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs) ++{ ++ setServiceImpl(srvcType, groups, privs); ++} ++ ++void UserService::updateServiceType(const ServiceType &srvcType, ++ const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs) ++{ ++ usrSrvcImpl.reset(); ++ setServiceImpl(srvcType, groups, privs); ++} ++ ++void UserService::setServiceImpl(const ServiceType &srvcType, ++ const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs) ++{ ++ switch (srvcType) ++ { ++ case ServiceType::shadow: ++ { ++ usrSrvcImpl = std::make_unique<ShadowService>(); ++ } ++ break; ++ ++ case ServiceType::sssd: ++ { ++ usrSrvcImpl = std::make_unique<SSSDService>(groups, privs); ++ } ++ break; ++ ++ case ServiceType::none: ++ default: ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Invalid service type initialization!"); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ break; ++ } ++} ++ ++UserService::~UserService() ++{ ++} ++ ++phosphor::user::UserSSHLists UserService::getUserAndSshGrpList() const ++{ ++ return usrSrvcImpl->getUserAndSshGrpList(); ++} ++ ++std::vector<std::string> ++ UserService::getUsersInGroup(const std::string &groupName) const ++{ ++ return usrSrvcImpl->getUsersInGroup(groupName); ++} ++ ++void UserService::createUser(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv, const bool &enabled) const ++{ ++ usrSrvcImpl->createUser(userName, groupNames, priv, enabled); ++} ++ ++void UserService::renameUser(const std::string &userName, ++ const std::string &newUserName) const ++{ ++ usrSrvcImpl->renameUser(userName, newUserName); ++} ++ ++void UserService::deleteUser(const std::string &userName) const ++{ ++ usrSrvcImpl->deleteUser(userName); ++} ++ ++void UserService::updateGroupsAndPriv( ++ const std::string &userName, const std::vector<std::string> &groupNames, ++ const std::string &priv) const ++{ ++ usrSrvcImpl->updateGroupsAndPriv(userName, groupNames, priv); ++} ++ ++void UserService::updateUserStatus(const std::string &userName, ++ const bool &enabled) const ++{ ++ usrSrvcImpl->updateUserStatus(userName, enabled); ++} ++ ++bool UserService::isUserEnabled(const std::string &userName) const ++{ ++ return usrSrvcImpl->isUserEnabled(userName); ++} ++ ++std::vector<std::string> ++ UserService::getUserGroups(const std::string &userName) const ++{ ++ return usrSrvcImpl->getUserGroups(userName); ++} ++ ++} // namespace user ++} // namespace phosphor +diff --git a/user_service.hpp b/user_service.hpp +new file mode 100644 +index 0000000..97a049b +--- /dev/null ++++ b/user_service.hpp +@@ -0,0 +1,233 @@ ++/* ++// Copyright (c) 2018 Intel Corporation ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++*/ ++ ++#pragma once ++#include <xyz/openbmc_project/Common/error.hpp> ++#include <xyz/openbmc_project/User/Common/error.hpp> ++#include <phosphor-logging/log.hpp> ++#include <phosphor-logging/elog.hpp> ++#include <boost/process/child.hpp> ++#include <boost/process/io.hpp> ++ ++namespace phosphor ++{ ++namespace user ++{ ++ ++using UserSSHLists = ++ std::pair<std::vector<std::string>, std::vector<std::string>>; ++using InternalFailure = ++ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; ++using InsufficientPermission = ++ sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission; ++ ++const std::string grpSsh = "ssh"; ++ ++template <typename... ArgTypes> ++std::vector<std::string> executeCmd(const char *path, ArgTypes &&... tArgs) ++{ ++ std::vector<std::string> stdOutput; ++ boost::process::ipstream stdOutStream; ++ boost::process::child execProg(path, const_cast<char *>(tArgs)..., ++ boost::process::std_out > stdOutStream); ++ std::string stdOutLine; ++ ++ while (stdOutStream && std::getline(stdOutStream, stdOutLine) && ++ !stdOutLine.empty()) ++ { ++ stdOutput.emplace_back(stdOutLine); ++ } ++ ++ execProg.wait(); ++ ++ int retCode = execProg.exit_code(); ++ if (retCode) ++ { ++ phosphor::logging::log<phosphor::logging::level::ERR>( ++ "Command execution failed", ++ phosphor::logging::entry("PATH=%d", path), ++ phosphor::logging::entry("RETURN_CODE:%d", retCode)); ++ phosphor::logging::elog<phosphor::user::InternalFailure>(); ++ } ++ ++ return stdOutput; ++} ++ ++/** @class UserServiceInterface ++ * @brief Interface class for methods provided by the implemmentations ++ * of the user service. Provides the same methods as the UserService ++ * class. ++ */ ++class UserServiceInterface ++{ ++ public: ++ UserServiceInterface() = default; ++ virtual ~UserServiceInterface() = default; ++ virtual UserSSHLists getUserAndSshGrpList() const = 0; ++ virtual std::vector<std::string> ++ getUsersInGroup(const std::string &groupName) const = 0; ++ virtual void createUser(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv, ++ const bool &enabled) const = 0; ++ virtual void renameUser(const std::string &userName, ++ const std::string &newUserName) const = 0; ++ virtual void deleteUser(const std::string &userName) const = 0; ++ virtual void updateGroupsAndPriv(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv) const = 0; ++ virtual void updateUserStatus(const std::string &userName, ++ const bool &enabled) const = 0; ++ virtual bool isUserEnabled(const std::string &userName) const = 0; ++ virtual std::vector<std::string> ++ getUserGroups(const std::string &userName) const = 0; ++ virtual void createGroup(const std::string &groupName) const = 0; ++}; ++ ++/** @class UserService ++ * @brief Responsible for managing the user service for the user manager. ++ * This service is the one responsible to actually change the user information ++ * of the application. It can support sevaral services, currently the ones ++ * supported are: ++ * ++ * 1) Shadow: Which uses the /etc/shadow file for updating the users ++ * 2) SSSD: Which uses the sssd service for a LOCAL domain only right now. ++ */ ++class UserService ++{ ++ public: ++ UserService() = delete; ++ UserService(const UserService &) = delete; ++ UserService &operator=(const UserService &) = delete; ++ UserService(UserService &&) = delete; ++ UserService &operator=(UserService &&) = delete; ++ ++ // Service Types implemented. None is used to validate. ++ enum class ServiceType ++ { ++ none, ++ shadow, ++ sssd ++ }; ++ ++ UserService(const ServiceType &srvcType, ++ const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs); ++ ~UserService(); ++ ++ /** @brief update the current Service type of the instance. ++ * This function is used to update in real time the service ++ * being used for the user management without restarting the ++ * whole service. ++ * ++ * @param[in] srvcType ++ * @param[in] groups ++ * @param[in] privs ++ */ ++ void updateServiceType(const ServiceType &srvcType, ++ const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs); ++ ++ /** @brief get user list and SSH group members list ++ * This method gets the list of users from the service. ++ * If the userlist reference is empty, all the users will be added ++ * and DBus notified about them. If the list is not empty, the function ++ * will only update list adding the missing ones to it. It will not remove ++ * any extra users on the list that are not part of the service! ++ * ++ */ ++ UserSSHLists getUserAndSshGrpList() const; ++ ++ /** @brief Get users in group. ++ * This method creates a new user as requested ++ * ++ * @param[in] groupName - Name of the group which has to be queried ++ */ ++ std::vector<std::string> ++ getUsersInGroup(const std::string &groupName) const; ++ ++ /** @brief create user method. ++ * This method creates a new user as requested ++ * ++ * @param[in] userName - Name of the user which has to be created ++ * @param[in] groupNames - Group names list, to which user has to be added. ++ * @param[in] priv - Privilege of the user. ++ * @param[in] enabled - State of the user enabled / disabled. ++ */ ++ void createUser(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv, const bool &enabled) const; ++ ++ /** @brief rename user method. ++ * This method renames the user as requested ++ * ++ * @param[in] userName - current name of the user ++ * @param[in] userName - user name to which it has to be renamed. ++ */ ++ void renameUser(const std::string &userName, ++ const std::string &newUserName) const; ++ ++ /** @brief delete user method. ++ * This method deletes the user as requested ++ * ++ * @param[in] userName - Name of the user which has to be deleted ++ */ ++ void deleteUser(const std::string &userName) const; ++ ++ /** @brief Updates user Groups and Privilege. ++ * ++ * @param[in] userName - Name of the user which has to be modified ++ * @param[in] groupNames - Group names list for user. ++ * @param[in] priv - Privilege of the user. ++ */ ++ void updateGroupsAndPriv(const std::string &userName, ++ const std::vector<std::string> &groupNames, ++ const std::string &priv) const; ++ ++ /** @brief Updates user status ++ * If enabled = false: User will be disabled ++ * If enabled = true : User will be enabled ++ * ++ * @param[in] userName - Name of the user ++ * @param[in] enabled - Status of the user: enabled / disabled? ++ */ ++ void updateUserStatus(const std::string &userName, ++ const bool &enabled) const; ++ ++ /** @brief Verify if user is enabled or not ++ * If enabled returns true ++ * If not enabled returns false ++ * ++ * @param[in] userName - Name of the user ++ */ ++ bool isUserEnabled(const std::string &userName) const; ++ ++ /** @brief Get the list of groups a user belongs to ++ * ++ * @param[in] userName - Name of the user ++ */ ++ std::vector<std::string> getUserGroups(const std::string &userName) const; ++ ++ private: ++ // User service implementation. ++ void setServiceImpl(const ServiceType &srvcType, ++ const std::vector<std::string> &groups, ++ const std::vector<std::string> &privs); ++ std::unique_ptr<UserServiceInterface> usrSrvcImpl; ++}; ++ ++} // namespace user ++} // namespace phosphor +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager_%.bbappend new file mode 100644 index 000000000..4a8952235 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/users/phosphor-user-manager_%.bbappend @@ -0,0 +1,5 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += " \ + file://0005-Added-suport-for-multiple-user-manager-services.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog.bb new file mode 100644 index 000000000..950f4932d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog.bb @@ -0,0 +1,35 @@ + +SUMMARY = "FRB2 timer service" +DESCRIPTION = "The FRB2 timer service will monitor the mailbox register 0\ +and start a watchdog for FRB2 if the data is 1(BIOS will write this value)" + +SRC_URI = "\ + file://CMakeLists.txt \ + file://frb2-watchdog.cpp \ + " +PV = "0.1" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${PHOSPHORBASE}/LICENSE;md5=19407077e42b1ba3d653da313f1f5b4e" + +S = "${WORKDIR}" + +inherit cmake +inherit pkgconfig pythonnative + +DEPENDS += " \ + systemd \ + sdbusplus \ + sdbusplus-native \ + phosphor-logging \ + phosphor-dbus-interfaces \ + phosphor-dbus-interfaces-native \ + boost \ + " + +RDEPENDS_${PN} += " \ + libsystemd \ + sdbusplus \ + phosphor-logging \ + phosphor-dbus-interfaces \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.clang-format b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.clang-format new file mode 100644 index 000000000..dd2770837 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.clang-format @@ -0,0 +1,98 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +PointerAlignment: Left +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^[<"](gtest|gmock)' + Priority: 5 + - Regex: '^"config.h"' + Priority: -1 + - Regex: '^".*\.hpp"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 + - Regex: '^<.*' + Priority: 3 + - Regex: '.*' + Priority: 4 +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/CMakeLists.txt new file mode 100644 index 000000000..bd5567d31 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required (VERSION 3.5 FATAL_ERROR) +project (frb2-watchdog CXX) +set (CMAKE_CXX_STANDARD 17) +set (CMAKE_CXX_STANDARD_REQUIRED ON) +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti") + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +# boost support +find_package (Boost REQUIRED) +# pkg_check_modules(Boost boost REQUIRED) +include_directories (${Boost_INCLUDE_DIRS}) +add_definitions (-DBOOST_ERROR_CODE_HEADER_ONLY) +add_definitions (-DBOOST_SYSTEM_NO_DEPRECATED) +add_definitions (-DBOOST_ALL_NO_LIB) +add_definitions (-DBOOST_NO_RTTI) +add_definitions (-DBOOST_NO_TYPEID) +add_definitions (-DBOOST_ASIO_DISABLE_THREADS) + +# import libsystemd +find_package (PkgConfig REQUIRED) +pkg_check_modules (SYSTEMD libsystemd REQUIRED) +include_directories (${SYSTEMD_INCLUDE_DIRS}) +link_directories (${SYSTEMD_LIBRARY_DIRS}) + +# import sdbusplus +find_package (PkgConfig REQUIRED) +pkg_check_modules (SDBUSPLUSPLUS sdbusplus REQUIRED) +include_directories (${SDBUSPLUSPLUS_INCLUDE_DIRS}) +link_directories (${SDBUSPLUSPLUS_LIBRARY_DIRS}) + +# import phosphor-logging +find_package (PkgConfig REQUIRED) +pkg_check_modules (LOGGING phosphor-logging REQUIRED) +include_directories (${LOGGING_INCLUDE_DIRS}) +link_directories (${LOGGING_LIBRARY_DIRS}) + +# import phosphor-dbus-interfaces +find_package (PkgConfig REQUIRED) +pkg_check_modules (DBUSINTERFACE phosphor-dbus-interfaces REQUIRED) +include_directories (${DBUSINTERFACE_INCLUDE_DIRS}) +link_directories (${DBUSINTERFACE_LIBRARY_DIRS}) + +add_executable (frb2-watchdog frb2-watchdog.cpp) + +target_link_libraries (${PROJECT_NAME} systemd) +target_link_libraries (${PROJECT_NAME} ${Boost_LIBRARIES}) +target_link_libraries (${PROJECT_NAME} ${SDBUSPLUSPLUS_LIBRARIES}) +target_link_libraries (${PROJECT_NAME} ${DBUSINTERFACE_LIBRARIES} + phosphor_logging) +install (TARGETS frb2-watchdog DESTINATION bin) diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/cmake-format.json b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/cmake-format.json new file mode 100644 index 000000000..583c255a3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/cmake-format.json @@ -0,0 +1,12 @@ +{ + "enum_char": ".", + "line_ending": "unix", + "bullet_char": "*", + "max_subargs_per_line": 99, + "command_case": "lower", + "tab_size": 4, + "line_width": 80, + "separate_fn_name_with_space": true, + "dangle_parens": true, + "separate_ctrl_name_with_space": true +}
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/frb2-watchdog.cpp b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/frb2-watchdog.cpp new file mode 100644 index 000000000..5356e95db --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/frb2-watchdog.cpp @@ -0,0 +1,258 @@ +/* Copyright 2018 Intel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fcntl.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <boost/container/flat_set.hpp> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <iostream> +#include <memory> +#include <optional> +#include <phosphor-logging/log.hpp> +#include <sdbusplus/asio/object_server.hpp> +#include <sdbusplus/bus.hpp> +#include <sdbusplus/bus/match.hpp> +#include <sdbusplus/message.hpp> +#include <sdbusplus/timer.hpp> +#include <vector> +#include <xyz/openbmc_project/State/Watchdog/server.hpp> + +void handleResponse(const boost::system::error_code &err, + std::size_t bytes_transferred); + +static int mailboxDevFd = -1; + +static boost::asio::io_service io; +static auto conn = std::make_shared<sdbusplus::asio::connection>(io); +boost::asio::ip::tcp::socket mailBoxDevSocket(io); +boost::asio::deadline_timer pollTimer(io); +boost::asio::posix::stream_descriptor inputDevice(io); + +// mailbox registre data[0:0] for FRB2 enable bit +boost::asio::streambuf readBuf(1); +std::string dataRead; + +// FRB2 watchdog timeout is 6 minutes +static constexpr unsigned int frb2TimerIntervalMs = 360 * 1000; + +// mailbox device polling time interval is 2 seconds +static constexpr unsigned int pollMs = 2000; + +static constexpr unsigned int frb2Started = 1; +static constexpr unsigned int frb2Stopped = 0; + +// FRB2 status +static uint8_t frb2Status = frb2Stopped; + +static constexpr const char *mailboxDevName = "/dev/aspeed-mbox"; + +static constexpr const char frb2Bus[] = "xyz.openbmc_project.FRB2"; +static constexpr const char frb2Obj[] = "/xyz/openbmc_project/FRB2"; +static constexpr const char frb2Intf[] = "xyz.openbmc_project.FRB2"; + +static constexpr char powerBus[] = "xyz.openbmc_project.Chassis.Control.Power"; +static constexpr char powerPath[] = + "/xyz/openbmc_project/Chassis/Control/Power0"; +static constexpr char powerIntf[] = "xyz.openbmc_project.Chassis.Control.Power"; + +static constexpr char wdBus[] = "xyz.openbmc_project.Watchdog"; +static constexpr char wdPath[] = "/xyz/openbmc_project/watchdog/host0"; +static constexpr char wdIntf[] = "xyz.openbmc_project.State.Watchdog"; +static constexpr char propIntf[] = "org.freedesktop.DBus.Properties"; + +typedef boost::asio::buffers_iterator<boost::asio::const_buffers_1> iterator; + +// check if FRB2 bit is 0x1 +std::pair<iterator, bool> matchFRB2(iterator begin, iterator end) +{ + unsigned char ch = 0; + iterator i = begin; + + while (i != end) + { + ch = static_cast<unsigned char>(*i); + if (ch & 0x1) + { + return std::make_pair(i, true); + } + i++; + } + + return std::make_pair(i, false); +} + +static void startRead() +{ + boost::asio::async_read_until(inputDevice, readBuf, matchFRB2, + [&](const boost::system::error_code &ec, + std::size_t bytes_transferred) { + handleResponse(ec, bytes_transferred); + }); +} + +template <typename T> void setProperty(const std::string &key, const T &val) +{ + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "setProperty", phosphor::logging::entry("KEY=%s", key.c_str())); + + try + { + conn->async_method_call( + [](const boost::system::error_code &err) { + if (err) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "async_method_call error!", + phosphor::logging::entry( + "ERROR=%s", + boost::system::system_error(err).what())); + } + }, + wdBus, wdPath, propIntf, "Set", wdIntf, key, + sdbusplus::message::variant_ns::variant<T>(val)); + } + catch (sdbusplus::exception::SdBusError &e) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "Dbus error!", phosphor::logging::entry("ERROR=%s", e.what())); + } +} +void handleResponse(const boost::system::error_code &err, + std::size_t bytes_transferred) +{ + std::istream responseStream(&readBuf); + std::string response; + int n = 0; + uint64_t interval = frb2TimerIntervalMs; + + std::getline(responseStream, response); + responseStream.clear(); + + if (err == boost::system::errc::bad_file_descriptor) + { + + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "bad file descriptor"); + return; // we're being destroyed + } + + if (!err) + { + // FRB2 is set by BIOS + if (frb2Stopped == frb2Status) + { + // start FRB2 watchdog + frb2Status = frb2Started; + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "FRB2 enable, start FRB2 watchdog"); + setProperty( + "ExpireAction", + std::string( + "xyz.openbmc_project.State.Watchdog.Action.HardReset")); + setProperty("Interval", interval); + setProperty("TimeRemaining", interval); + setProperty("Initialized", true); + setProperty("Enabled", true); + } + } + else if (err == boost::asio::error::misc_errors::not_found) + { + // FRB2 is clear, stop FRB2 watchdog if it is started + if (frb2Started == frb2Status) + { + frb2Status = frb2Stopped; + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "FRB2 is unset, stop FRB2 watchdog"); + setProperty("Enabled", false); + } + } + else + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "handleResponse error!", + phosphor::logging::entry("ERROR=%s", + boost::system::system_error(err).what())); + } + + pollTimer.expires_from_now(boost::posix_time::milliseconds(pollMs)); + pollTimer.async_wait( + [](const boost::system::error_code &ec) { startRead(); }); +} + +int main(int argc, char **argv) +{ + phosphor::logging::log<phosphor::logging::level::DEBUG>( + "Monitor FRB2 signal"); + + sdbusplus::bus::match_t biosPostSignal( + static_cast<sdbusplus::bus::bus &>(*conn), + sdbusplus::bus::match::rules::type::signal() + + sdbusplus::bus::match::rules::member("PostCompleted") + + sdbusplus::bus::match::rules::path(powerPath) + + sdbusplus::bus::match::rules::interface(powerIntf), + [](sdbusplus::message::message &msg) { + uint8_t value = 0; + ssize_t rc = 0; + phosphor::logging::log<phosphor::logging::level::INFO>( + "BIOS post completed signal"); + // stop FRB2 and clean mailbox + value = 0; + rc = ::pwrite(mailboxDevFd, &value, 1, 0); + if (rc != 1) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "mailbox write error!"); + } + setProperty("Enabled", false); + frb2Status = frb2Stopped; + return; + }); + + conn->request_name(frb2Bus); + + auto server = sdbusplus::asio::object_server(conn); + + std::shared_ptr<sdbusplus::asio::dbus_interface> frb2Iface = + server.add_interface(frb2Obj, frb2Intf); + + frb2Iface->register_property("frb2Status", frb2Status); + + frb2Iface->initialize(); + + mailboxDevFd = ::open(mailboxDevName, O_RDWR | O_CLOEXEC); + if (mailboxDevFd < 0) + { + phosphor::logging::log<phosphor::logging::level::ERR>( + "mailbox device open fail!"); + return -1; + } + + inputDevice.assign(mailboxDevFd); + + startRead(); + + io.run(); + + ::close(mailboxDevFd); + + return 0; +} diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/obmc-enable-host-watchdog@.service b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/obmc-enable-host-watchdog@.service new file mode 100644 index 000000000..87a662f7c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/obmc-enable-host-watchdog@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Start FRB2 Watchdog%i +Wants=obmc-host-started@%i.target +After=obmc-host-started@%i.target +Wants=mapper-wait@-xyz-openbmc_project-watchdog-host%i.service +After=mapper-wait@-xyz-openbmc_project-watchdog-host%i.service +Conflicts=obmc-host-stop@%i.target +ConditionPathExists=!/run/openbmc/host@%i-on + +[Service] +Restart=always +ExecStart=/usr/bin/env frb2-watchdog +ExecStopPost=/bin/sh -c "busctl call `mapper get-service /xyz/openbmc_project/watchdog/host%i` /xyz/openbmc_project/watchdog/host%i org.freedesktop.DBus.Properties Set ssv xyz.openbmc_project.State.Watchdog Enabled b false" +SyslogIdentifier=obmc-enable-host-watchdog diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend new file mode 100644 index 000000000..d1888e86f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend @@ -0,0 +1,4 @@ +FILESEXTRAPATHS_append := ":${THISDIR}/${PN}" + +# Remove the override to keep service running after DC cycle +SYSTEMD_OVERRIDE_${PN}_remove = "poweron.conf:phosphor-watchdog@poweron.service.d/poweron.conf" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb new file mode 100644 index 000000000..5da053f1d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb @@ -0,0 +1,13 @@ +SUMMARY = "System watchdog" +DESCRIPTION = "BMC hardware watchdog service that is used to reset BMC \ + when unrecoverable events occurs" + +inherit allarch +inherit obmc-phosphor-systemd + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" + +SYSTEMD_SERVICE_${PN} += "system-watchdog.service" +SYSTEMD_ENVIRONMENT_FILE_${PN} += "obmc/system-watchdog/system-watchdog.conf" + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf new file mode 100644 index 000000000..defe830a1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf @@ -0,0 +1,3 @@ +TIMEOUT=60 +INTERVAL=10 +DEVICE=/dev/watchdog1 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service new file mode 100644 index 000000000..1564fda20 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service @@ -0,0 +1,11 @@ +[Unit] +Description=BMC Hardware Watchdog Daemon + +[Service] +EnvironmentFile=/etc/default/obmc/system-watchdog/system-watchdog.conf +ExecStart=/sbin/watchdog -T ${{TIMEOUT}} -t ${{INTERVAL}} -F ${{DEVICE}} +KillSignal=SIGKILL + +[Install] +WantedBy=basic.target + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/0001-Implement-KVM-in-webui.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/0001-Implement-KVM-in-webui.patch new file mode 100644 index 000000000..a584c473c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui/0001-Implement-KVM-in-webui.patch @@ -0,0 +1,242 @@ +From a129c4e92eebd03772e5f68a2fcf855e00874f19 Mon Sep 17 00:00:00 2001 +From: Ed tanous <ed@tanous.net> +Date: Sun, 22 Apr 2018 10:53:28 -0700 +Subject: [PATCH] Implement KVM in webui + +This patchset adds the infrastructure to allow KVM sessions +through the webui. A websocket capable VNC/RFB connection +on the BMC is needed for KVM sessions. + +To access, navigate to Server control -> KVM. + +Tested: Ran obmc-ikvm on the BMC, added a KVM Handler to + Phosphor Rest Server, and was able to establish a + KVM session in the webui on a Witherspoon. +Change-Id: I7dda5bec41d270ae8d0913697714d4df4ec3a257 +Signed-off-by: Ed Tanous <ed.tanous@intel.com> +Signed-off-by: Gunnar Mills <gmills@us.ibm.com> +--- + app/common/directives/app-navigation.html | 12 +++-- + app/index.js | 1 + + app/server-control/controllers/kvm-controller.html | 5 ++ + app/server-control/controllers/kvm-controller.js | 55 ++++++++++++++++++++++ + app/server-control/index.js | 5 ++ + app/server-control/styles/index.scss | 1 + + app/server-control/styles/kvm.scss | 11 +++++ + package-lock.json | 5 ++ + package.json | 5 +- + webpack.config.js | 6 ++- + 10 files changed, 98 insertions(+), 8 deletions(-) + create mode 100644 app/server-control/controllers/kvm-controller.html + create mode 100644 app/server-control/controllers/kvm-controller.js + create mode 100644 app/server-control/styles/kvm.scss + +diff --git a/app/common/directives/app-navigation.html b/app/common/directives/app-navigation.html +index 2f3ded76cad2..f8b5db742acc 100644 +--- a/app/common/directives/app-navigation.html ++++ b/app/common/directives/app-navigation.html +@@ -85,19 +85,21 @@ + <a href="#/server-control/bmc-reboot" tabindex="12" ng-click="closeSubnav()">Reboot BMC</a></li> + <li ng-class="{'active': (path == '/server-control/remote-console')}"> + <a href="#/server-control/remote-console" tabindex="13" ng-click="closeSubnav()">Serial over LAN console</a></li> ++ <li ng-class="{'active': (path == '/server-control/kvm')}"> ++ <a href="#/server-control/kvm" tabindex="14" ng-click="closeSubnav()">KVM</a></li> + </ul> + <ul class="nav__second-level btn-firmware" ng-style="navStyle" ng-class="{opened: (showSubMenu && firstLevel == 'configuration')}"> + <li ng-class="{'active': (path == '/configuration' || path == '/configuration/network')}"> +- <a href="#/configuration/network" tabindex="14" ng-click="closeSubnav()">Network settings</a></li> ++ <a href="#/configuration/network" tabindex="15" ng-click="closeSubnav()">Network settings</a></li> + <li ng-class="{'active': (path == '/configuration' || path == '/configuration/snmp')}"> +- <a href="#/configuration/snmp" tabindex="15" ng-click="closeSubnav()">SNMP settings</a></li> ++ <a href="#/configuration/snmp" tabindex="16" ng-click="closeSubnav()">SNMP settings</a></li> + <li ng-class="{'active': (path == '/configuration' || path == '/configuration/firmware')}"> +- <a href="#/configuration/firmware" tabindex="16" ng-click="closeSubnav()">Firmware</a></li> ++ <a href="#/configuration/firmware" tabindex="17" ng-click="closeSubnav()">Firmware</a></li> + <li ng-class="{'active': (path == '/configuration' || path == '/configuration/date-time')}"> +- <a href="#/configuration/date-time" tabindex="17" ng-click="closeSubnav()">Date and time settings</a></li> ++ <a href="#/configuration/date-time" tabindex="18" ng-click="closeSubnav()">Date and time settings</a></li> + </ul> + <ul class="nav__second-level btn-users" ng-style="navStyle" ng-class="{opened: (showSubMenu && firstLevel == 'users')}"> + <li ng-class="{'active': (path == '/users' || path == '/users/manage-accounts')}"> +- <a href="#/users/manage-accounts" tabindex="18" ng-click="closeSubnav()">Manage user accounts</a></li> ++ <a href="#/users/manage-accounts" tabindex="19" ng-click="closeSubnav()">Manage user account</a></li> + </ul> + </nav> +diff --git a/app/index.js b/app/index.js +index c9fed83fe4a9..d6b4a08fa5c6 100644 +--- a/app/index.js ++++ b/app/index.js +@@ -69,6 +69,7 @@ import power_operations_controller from './server-control/controllers/power-oper + import power_usage_controller from './server-control/controllers/power-usage-controller.js'; + import remote_console_window_controller from './server-control/controllers/remote-console-window-controller.js'; + import server_led_controller from './server-control/controllers/server-led-controller.js'; ++import kvm_controller from './server-control/controllers/kvm-controller.js'; + + import server_health_index from './server-health/index.js'; + import inventory_overview_controller from './server-health/controllers/inventory-overview-controller.js'; +diff --git a/app/server-control/controllers/kvm-controller.html b/app/server-control/controllers/kvm-controller.html +new file mode 100644 +index 000000000000..40e4d97454bc +--- /dev/null ++++ b/app/server-control/controllers/kvm-controller.html +@@ -0,0 +1,5 @@ ++<div id="noVNC_container"> ++ <div id="noVNC_status_bar"> ++ <div id="noVNC_left_dummy_elem"></div> ++ </div> ++</div> +diff --git a/app/server-control/controllers/kvm-controller.js b/app/server-control/controllers/kvm-controller.js +new file mode 100644 +index 000000000000..a43f169ddf19 +--- /dev/null ++++ b/app/server-control/controllers/kvm-controller.js +@@ -0,0 +1,55 @@ ++/** ++ * Controller for KVM (Kernel-based Virtual Machine) ++ * ++ * @module app/serverControl ++ * @exports kvmController ++ * @name kvmController ++ */ ++ ++import RFB from '@novnc/novnc/core/rfb.js'; ++ ++window.angular && (function(angular) { ++ 'use strict'; ++ ++ angular.module('app.serverControl').controller('kvmController', [ ++ '$scope', '$location', '$log', ++ function($scope, $location, $log) { ++ var rfb; ++ ++ $scope.$on('$destroy', function() { ++ if (rfb) { ++ rfb.disconnect(); ++ } ++ }); ++ ++ function sendCtrlAltDel() { ++ rfb.sendCtrlAltDel(); ++ return false; ++ }; ++ ++ function connected(e) { ++ $log.debug('RFB Connected'); ++ } ++ function disconnected(e) { ++ $log.debug('RFB disconnected'); ++ } ++ ++ var host = $location.host(); ++ var port = $location.port(); ++ var target = ++ angular.element(document.querySelector('#noVNC_container'))[0]; ++ ++ try { ++ rfb = new RFB(target, 'wss://' + host + ':' + port + '/kvm/0', {}); ++ ++ rfb.addEventListener('connect', connected); ++ rfb.addEventListener('disconnect', disconnected); ++ } catch (exc) { ++ $log.error(exc); ++ updateState( ++ null, 'fatal', null, 'Unable to create RFB client -- ' + exc); ++ return; // don't continue trying to connect ++ }; ++ } ++ ]); ++})(angular); +diff --git a/app/server-control/index.js b/app/server-control/index.js +index 739bd1eb8ad9..1b8aad50b702 100644 +--- a/app/server-control/index.js ++++ b/app/server-control/index.js +@@ -48,6 +48,11 @@ window.angular && (function(angular) { + 'controller': 'remoteConsoleWindowController', + authenticated: true + }) ++ .when('/server-control/kvm', { ++ 'template': require('./controllers/kvm-controller.html'), ++ 'controller': 'kvmController', ++ authenticated: true ++ }) + .when('/server-control', { + 'template': + require('./controllers/power-operations-controller.html'), +diff --git a/app/server-control/styles/index.scss b/app/server-control/styles/index.scss +index f6b15ab6afc9..5e8a99580894 100644 +--- a/app/server-control/styles/index.scss ++++ b/app/server-control/styles/index.scss +@@ -3,3 +3,4 @@ + @import "./remote-console.scss"; + @import "./server-led.scss"; + @import "./power-usage.scss"; ++@import "./kvm.scss"; +diff --git a/app/server-control/styles/kvm.scss b/app/server-control/styles/kvm.scss +new file mode 100644 +index 000000000000..2f9e2c0c9f37 +--- /dev/null ++++ b/app/server-control/styles/kvm.scss +@@ -0,0 +1,11 @@ ++ ++.noNVC_shown { ++ display: inline; ++} ++.noVNC_hidden { ++ display: none; ++} ++ ++#noVNC_left_dummy_elem { ++ flex: 1; ++} +diff --git a/package-lock.json b/package-lock.json +index 2d9d31b21968..103c9b84b933 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -807,6 +807,11 @@ + "to-fast-properties": "2.0.0" + } + }, ++ "@novnc/novnc": { ++ "version": "1.0.0", ++ "resolved": "https://registry.npmjs.org/@novnc/novnc/-/novnc-1.0.0.tgz", ++ "integrity": "sha1-drDonm+HOMqBVBlbr1uOaoC8kQU=" ++ }, + "@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", +diff --git a/package.json b/package.json +index 35c6b78e320c..c0125a0b72fd 100644 +--- a/package.json ++++ b/package.json +@@ -28,8 +28,9 @@ + "node" + ], + "dependencies": { +- "angular": "^1.7.5", +- "angular-animate": "^1.7.5", ++ "@novnc/novnc": "^1.0.0", ++ "angular": "^1.7.3", ++ "angular-animate": "^1.7.3", + "angular-clipboard": "^1.6.2", + "angular-cookies": "^1.7.5", + "angular-messages": "^1.7.6", +diff --git a/webpack.config.js b/webpack.config.js +index 91cbea8f2952..6c8667cbbc98 100644 +--- a/webpack.config.js ++++ b/webpack.config.js +@@ -113,7 +113,11 @@ module.exports = (env, options) => { + 'base-uri': '\'self\'', + 'object-src': '\'none\'', + 'script-src': ['\'self\''], +- 'style-src': ['\'self\''] ++ 'style-src': ['\'self\''], ++ // KVM requires image buffers from data: payloads, so allow that in ++ // img-src ++ // https://stackoverflow.com/questions/18447970/content-security-policy-data-not-working-for-base64-images-in-chrome-28 ++ 'img-src': ['\'self\'', 'data:'], + }), + new MiniCssExtractPlugin(), + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui_%.bbappend new file mode 100644 index 000000000..e40b5ed8e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/phosphor-webui_%.bbappend @@ -0,0 +1,3 @@ +FILESEXTRAPATHS_append := ":${THISDIR}/${PN}" + +SRC_URI += "file://0001-Implement-KVM-in-webui.patch" diff --git a/meta-openbmc-mods/meta-common/recipes-security/sssd/files/ldb.sh b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/ldb.sh new file mode 100644 index 000000000..176bfd7ca --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/ldb.sh @@ -0,0 +1 @@ +export LDB_MODULES_PATH=/usr/lib/ldb diff --git a/meta-openbmc-mods/meta-common/recipes-security/sssd/files/locked_groups b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/locked_groups new file mode 100644 index 000000000..7c189e231 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/locked_groups @@ -0,0 +1 @@ +sssd_locked diff --git a/meta-openbmc-mods/meta-common/recipes-security/sssd/files/nscd.conf b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/nscd.conf new file mode 100644 index 000000000..d2ffe5ddc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/nscd.conf @@ -0,0 +1,2 @@ +enable-cache passwd no +enable-cache group no
\ No newline at end of file diff --git a/meta-openbmc-mods/meta-common/recipes-security/sssd/files/sssd.conf b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/sssd.conf new file mode 100644 index 000000000..7a2786bee --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/sssd.conf @@ -0,0 +1,16 @@ +[sssd] +domains = LOCAL +services = nss, pam +config_file_version = 2 + +[nss] +enum_cache_timeout = 1 +filter_groups = root +filter_users = root + +[pam] + +[domain/LOCAL] +enumerate = true +id_provider = local +auth_provider = local diff --git a/meta-openbmc-mods/meta-common/recipes-security/sssd/files/sssd.service b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/sssd.service new file mode 100644 index 000000000..fe2bcf8b4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-security/sssd/files/sssd.service @@ -0,0 +1,15 @@ +[Unit] +Description=System Security Services Daemon +# SSSD must be running before we permit user sessions +Before=systemd-user-sessions.service nss-user-lookup.target +Wants=nss-user-lookup.target + +[Service] +Environment=LDB_MODULES_PATH=/usr/lib/ldb DEBUG_LOGGER=-f +ExecStart=/usr/sbin/sssd $DEBUG_LOGGER +Type=simple +Restart=always +PIDFile=/var/run/sssd.pid + +[Install] +WantedBy=multi-user.target diff --git a/meta-openbmc-mods/meta-common/recipes-security/sssd/sssd_%.bbappend b/meta-openbmc-mods/meta-common/recipes-security/sssd/sssd_%.bbappend new file mode 100644 index 000000000..03965ce72 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-security/sssd/sssd_%.bbappend @@ -0,0 +1,26 @@ +inherit obmc-phosphor-systemd + +FILESEXTRAPATHS_append := "${THISDIR}/files:" +SRC_URI += "file://sssd.conf \ + file://nscd.conf \ + file://locked_groups \ + file://ldb.sh \ + " + +PACKAGECONFIG += " systemd " +SYSTEMD_AUTO_ENABLE = "enable" + +EXTRA_OECONF += " --enable-pammoddir=${base_libdir}/security" + +do_install_append() { + # sssd creates also the /var/run link. Need to remove it to avoid conflicts + # with the one created by base-files recipe. + rm -rf ${D}/var/run + install -m 600 ${WORKDIR}/locked_groups ${D}/${sysconfdir}/${BPN} + install -m 600 ${WORKDIR}/nscd.conf ${D}/${sysconfdir} + install -d ${D}${sysconfdir}/profile.d + install -m 0644 ${WORKDIR}/ldb.sh ${D}${sysconfdir}/profile.d +} + +FILES_${PN} += " /lib/security/pam_sss.so " + diff --git a/meta-openbmc-mods/meta-common/recipes-support/libgpiod/libgpiod/0001-Add-pass-through-setting-in-gpioset.patch b/meta-openbmc-mods/meta-common/recipes-support/libgpiod/libgpiod/0001-Add-pass-through-setting-in-gpioset.patch new file mode 100644 index 000000000..b90d42ca3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-support/libgpiod/libgpiod/0001-Add-pass-through-setting-in-gpioset.patch @@ -0,0 +1,265 @@ +From 380a088dbe9fdd69e98bf6da8086e3d664da8338 Mon Sep 17 00:00:00 2001 +From: Kuiying Wang <kuiying.wang@intel.com> +Date: Thu, 31 Jan 2019 22:28:48 +0800 +Subject: [PATCH] Add pass through setting in gpioset + +Signed-off-by: Kuiying Wang <kuiying.wang@intel.com> +--- + aclocal.m4 | 2 +- + configure | 2 +- + include/gpiod.h | 10 ++++++++-- + src/lib/core.c | 13 +++++++------ + src/lib/ctxless.c | 8 ++++---- + src/lib/helpers.c | 3 ++- + src/tools/gpioset.c | 13 +++++++++---- + tests/tests-ctxless.c | 2 +- + 8 files changed, 33 insertions(+), 20 deletions(-) + +diff --git a/aclocal.m4 b/aclocal.m4 +index b0db596..a5e28ad 100644 +--- a/aclocal.m4 ++++ b/aclocal.m4 +@@ -911,7 +911,7 @@ AS_VAR_IF([$1], [""], [$5], [$4])dnl + # generated from the m4 files accompanying Automake X.Y. + # (This private macro should not be called outside this file.) + AC_DEFUN([AM_AUTOMAKE_VERSION], +-[am__api_version='1.15' ++[am__api_version='1.16' + dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to + dnl require some minimum version. Point them to the right macro. + m4_if([$1], [1.15], [], +diff --git a/configure b/configure +index 3d44cd1..d218338 100755 +--- a/configure ++++ b/configure +@@ -2477,7 +2477,7 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + + +-am__api_version='1.15' ++am__api_version='1.16' + + # Find a good install program. We prefer a C program (faster), + # so one script is as good as another. But avoid the broken or +diff --git a/include/gpiod.h b/include/gpiod.h +index ccff977..0f935e6 100644 +--- a/include/gpiod.h ++++ b/include/gpiod.h +@@ -121,6 +121,7 @@ typedef void (*gpiod_ctxless_set_value_cb)(void *); + * @param offset The offset of the GPIO line. + * @param value New value (0 or 1). + * @param active_low The active state of this line - true if low. ++ * @param pass_through The pass-through state of the lines - true if enabled. + * @param consumer Name of the consumer. + * @param cb Optional callback function that will be called right after setting + * the value. Users can use this, for example, to pause the execution +@@ -129,7 +130,7 @@ typedef void (*gpiod_ctxless_set_value_cb)(void *); + * @return 0 if the operation succeeds, -1 on error. + */ + int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value, +- bool active_low, const char *consumer, ++ bool active_low, bool pass_through, const char *consumer, + gpiod_ctxless_set_value_cb cb, + void *data) GPIOD_API; + +@@ -140,6 +141,7 @@ int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value, + * @param values Array of integers containing new values. + * @param num_lines Number of lines, must be > 0. + * @param active_low The active state of the lines - true if low. ++ * @param pass_through The pass-through state of the lines - true if enabled. + * @param consumer Name of the consumer. + * @param cb Optional callback function that will be called right after setting + * all values. Works the same as in ::gpiod_ctxless_set_value. +@@ -149,7 +151,7 @@ int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value, + int gpiod_ctxless_set_value_multiple(const char *device, + const unsigned int *offsets, + const int *values, unsigned int num_lines, +- bool active_low, const char *consumer, ++ bool active_low, bool pass_through, const char *consumer, + gpiod_ctxless_set_value_cb cb, + void *data) GPIOD_API; + +@@ -766,6 +768,8 @@ enum { + /**< Request the line(s) for reading the GPIO line state. */ + GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, + /**< Request the line(s) for setting the GPIO line state. */ ++ GPIOD_LINE_REQUEST_DIRECTION_PASS_THROUGH, ++ /**< Request the line(s) for setting the GPIO line state. */ + GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE, + /**< Monitor both types of events. */ + GPIOD_LINE_REQUEST_EVENT_RISING_EDGE, +@@ -784,6 +788,8 @@ enum { + /**< The line is an open-source port. */ + GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW = GPIOD_BIT(2), + /**< The active state of the line is low (high is the default). */ ++ GPIOD_LINE_REQUEST_FLAG_PASS_THROUGH = GPIOD_BIT(5), ++ /**< The line is a pass-through port*/ + }; + + /** +diff --git a/src/lib/core.c b/src/lib/core.c +index 4f273e3..feec362 100644 +--- a/src/lib/core.c ++++ b/src/lib/core.c +@@ -472,7 +472,6 @@ static int line_request_values(struct gpiod_line_bulk *bulk, + struct gpiohandle_request req; + unsigned int i; + int rv, fd; +- + if ((config->request_type != GPIOD_LINE_REQUEST_DIRECTION_OUTPUT) && + (config->flags & (GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN | + GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE))) { +@@ -499,14 +498,15 @@ static int line_request_values(struct gpiod_line_bulk *bulk, + req.flags |= GPIOHANDLE_REQUEST_INPUT; + else if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT) + req.flags |= GPIOHANDLE_REQUEST_OUTPUT; +- ++ else if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_PASS_THROUGH) ++ req.flags |= GPIOHANDLE_REQUEST_PASS_THROUGH; + req.lines = gpiod_line_bulk_num_lines(bulk); + + gpiod_line_bulk_foreach_line_off(bulk, line, i) { + req.lineoffsets[i] = gpiod_line_offset(line); +- if (config->request_type == +- GPIOD_LINE_REQUEST_DIRECTION_OUTPUT && +- default_vals) ++ if ((config->request_type == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT || ++ config->request_type == GPIOD_LINE_REQUEST_DIRECTION_PASS_THROUGH)&& ++ default_vals) + req.default_values[i] = !!default_vals[i]; + } + +@@ -617,7 +617,8 @@ static bool line_request_is_direction(int request) + { + return request == GPIOD_LINE_REQUEST_DIRECTION_AS_IS || + request == GPIOD_LINE_REQUEST_DIRECTION_INPUT || +- request == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; ++ request == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT || ++ request == GPIOD_LINE_REQUEST_DIRECTION_PASS_THROUGH; + } + + static bool line_request_is_events(int request) +diff --git a/src/lib/ctxless.c b/src/lib/ctxless.c +index 0009504..a16207e 100644 +--- a/src/lib/ctxless.c ++++ b/src/lib/ctxless.c +@@ -76,17 +76,17 @@ int gpiod_ctxless_get_value_multiple(const char *device, + } + + int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value, +- bool active_low, const char *consumer, ++ bool active_low, bool pass_through, const char *consumer, + gpiod_ctxless_set_value_cb cb, void *data) + { + return gpiod_ctxless_set_value_multiple(device, &offset, &value, 1, +- active_low, consumer, cb, data); ++ active_low, pass_through, consumer, cb, data); + } + + int gpiod_ctxless_set_value_multiple(const char *device, + const unsigned int *offsets, + const int *values, unsigned int num_lines, +- bool active_low, const char *consumer, ++ bool active_low, bool pass_through, const char *consumer, + gpiod_ctxless_set_value_cb cb, void *data) + { + struct gpiod_line_bulk bulk; +@@ -117,7 +117,7 @@ int gpiod_ctxless_set_value_multiple(const char *device, + } + + flags = active_low ? GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW : 0; +- ++ flags |= pass_through ? GPIOD_LINE_REQUEST_FLAG_PASS_THROUGH : 0; + status = gpiod_line_request_bulk_output_flags(&bulk, consumer, + flags, values); + if (status < 0) { +diff --git a/src/lib/helpers.c b/src/lib/helpers.c +index 80b8eff..807fb93 100644 +--- a/src/lib/helpers.c ++++ b/src/lib/helpers.c +@@ -362,7 +362,8 @@ int gpiod_line_request_bulk_output_flags(struct gpiod_line_bulk *bulk, + .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, + .flags = flags, + }; +- ++ if (flags & GPIOD_LINE_REQUEST_FLAG_PASS_THROUGH) ++ config.request_type = GPIOD_LINE_REQUEST_DIRECTION_PASS_THROUGH; + return gpiod_line_request_bulk(bulk, &config, default_vals); + } + +diff --git a/src/tools/gpioset.c b/src/tools/gpioset.c +index fb012fa..d5f0b77 100644 +--- a/src/tools/gpioset.c ++++ b/src/tools/gpioset.c +@@ -22,7 +22,8 @@ + static const struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, +- { "active-low", no_argument, NULL, 'l' }, ++ { "active-low", no_argument, NULL, 'l' }, ++ { "pass-through", no_argument, NULL, 'p' }, + { "mode", required_argument, NULL, 'm' }, + { "sec", required_argument, NULL, 's' }, + { "usec", required_argument, NULL, 'u' }, +@@ -30,7 +31,7 @@ static const struct option longopts[] = { + { GETOPT_NULL_LONGOPT }, + }; + +-static const char *const shortopts = "+hvlm:s:u:b"; ++static const char *const shortopts = "+hvlpm:s:u:b"; + + static void print_help(void) + { +@@ -40,8 +41,9 @@ static void print_help(void) + printf("\n"); + printf("Options:\n"); + printf(" -h, --help:\t\tdisplay this message and exit\n"); +- printf(" -v, --version:\tdisplay the version and exit\n"); + printf(" -l, --active-low:\tset the line active state to low\n"); ++ printf(" -v, --version:\tdisplay the version and exit\n"); ++ printf(" -p, --pass-through:\tset it to pass through mode\n"); + printf(" -m, --mode=[exit|wait|time|signal] (defaults to 'exit'):\n"); + printf(" tell the program what to do after setting values\n"); + printf(" -s, --sec=SEC:\tspecify the number of seconds to wait (only valid for --mode=time)\n"); +@@ -179,6 +181,7 @@ int main(int argc, char **argv) + int *values, status, optc, opti; + struct callback_data cbdata; + bool active_low = false; ++ bool pass_through = false; + char *device, *end; + + memset(&cbdata, 0, sizeof(cbdata)); +@@ -197,6 +200,8 @@ int main(int argc, char **argv) + return EXIT_SUCCESS; + case 'l': + active_low = true; ++ case 'p': ++ pass_through = true; + break; + case 'm': + mode = parse_mode(optarg); +@@ -263,7 +268,7 @@ int main(int argc, char **argv) + } + + status = gpiod_ctxless_set_value_multiple(device, offsets, values, +- num_lines, active_low, ++ num_lines, active_low, pass_through, + "gpioset", mode->callback, + &cbdata); + if (status < 0) +diff --git a/tests/tests-ctxless.c b/tests/tests-ctxless.c +index ea9403d..228c49d 100644 +--- a/tests/tests-ctxless.c ++++ b/tests/tests-ctxless.c +@@ -20,7 +20,7 @@ static void ctxless_set_get_value(void) + TEST_ASSERT_EQ(ret, 0); + + ret = gpiod_ctxless_set_value(test_chip_name(0), 3, 1, +- false, TEST_CONSUMER, NULL, NULL); ++ false, false, TEST_CONSUMER, NULL, NULL); + TEST_ASSERT_RET_OK(ret); + + ret = gpiod_ctxless_get_value(test_chip_name(0), 3, +-- +2.19.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-support/libgpiod/libgpiod_%.bbappend b/meta-openbmc-mods/meta-common/recipes-support/libgpiod/libgpiod_%.bbappend new file mode 100644 index 000000000..aa28e8d85 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-support/libgpiod/libgpiod_%.bbappend @@ -0,0 +1,5 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI += " \ + file://0001-Add-pass-through-setting-in-gpioset.patch \ + " diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/beeper-test.bb b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/beeper-test.bb new file mode 100644 index 000000000..51cda82e9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/beeper-test.bb @@ -0,0 +1,17 @@ +SUMMARY = "Beeper Test App" +DESCRIPTION = "Beeper Test Application for pwm-beeper" + +inherit cmake + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM += "\ + file://beeper-test.cpp;beginline=2;endline=14;md5=c451359f18a13ee69602afce1588c01a \ + " + +SRC_URI = "\ + file://CMakeLists.txt \ + file://beeper-test.cpp \ + " + +S = "${WORKDIR}" + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/.clang-format b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/.clang-format new file mode 100644 index 000000000..ea71ad6e1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/.clang-format @@ -0,0 +1,99 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +PointerAlignment: Left +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^[<"](gtest|gmock)' + Priority: 5 + - Regex: '^"config.h"' + Priority: -1 + - Regex: '^".*\.hpp"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 + - Regex: '^<.*' + Priority: 3 + - Regex: '.*' + Priority: 4 +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/CMakeLists.txt new file mode 100644 index 000000000..81a0c7e81 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR) +project(beeper-test CXX) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +add_executable(beeper-test beeper-test.cpp) +install(TARGETS beeper-test DESTINATION bin) + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/beeper-test.cpp b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/beeper-test.cpp new file mode 100644 index 000000000..31dafdd88 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/beeper-test/files/beeper-test.cpp @@ -0,0 +1,81 @@ +/* +// Copyright (c) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Abstract: pwm-beeper test application +// +*/ + +#include <fcntl.h> +#include <linux/input.h> +#include <stdio.h> +#include <unistd.h> + +#include <cstring> +#include <iostream> + +int main(int argc, char** argv) +{ + if (argc < 3) + { + std::cout << "usage: <input device> <sequence of 'tone in " + "Hz','duration in ms' pair>\n"; + std::cout << "example: beeper-test /dev/input/event0 " + "2100,100,0,150,2500,50,0,50,2200,100\n"; + return 1; + } + + int fd; + if ((fd = open(argv[1], O_RDWR | O_CLOEXEC)) < 0) + { + perror("Failed to open input device"); + return -1; + } + + struct input_event event; + event.type = EV_SND; + event.code = SND_TONE; + + char* pch = strtok(argv[2], ","); + while (pch != NULL) + { + event.value = atoi(pch); + + pch = strtok(NULL, ","); + if (!pch) + { + std::cerr << "Invalid tone,duration pair\n"; + close(fd); + return -1; + } + + int durationMs = atoi(pch); + + if (write(fd, &event, sizeof(struct input_event)) != + sizeof(struct input_event)) + { + perror("Failed to write a tone sound event"); + close(fd); + return -1; + } + + usleep(durationMs * 1000); + + pch = strtok(NULL, ","); + } + + close(fd); + + return 0; +} diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/dimmsensor.bb b/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/dimmsensor.bb new file mode 100644 index 000000000..78240c0f4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/dimmsensor.bb @@ -0,0 +1,17 @@ +SUMMARY = "Dimm Sensor" +DESCRIPTION = "Dimm Sensor Executable" + +SRC_URI = "\ + file://CMakeLists.txt \ + file://DimmSensor.cpp \ + " + +LICENSE = "CLOSED" + +S = "${WORKDIR}" + +inherit cmake + +# linux-libc-headers guides this way to include custom uapi headers +CXXFLAGS_append = " -I ${STAGING_KERNEL_DIR}/include/uapi" +do_configure[depends] += "virtual/kernel:do_shared_workdir" diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/files/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/files/CMakeLists.txt new file mode 100644 index 000000000..6262f8dee --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/files/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR) +project(dimmsensor CXX) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +add_executable(dimmsensor DimmSensor.cpp) +install (TARGETS dimmsensor DESTINATION bin) + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/files/DimmSensor.cpp b/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/files/DimmSensor.cpp new file mode 100644 index 000000000..9cc13c2a5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/dimmsensor/files/DimmSensor.cpp @@ -0,0 +1,143 @@ +#include <fcntl.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <iostream> +#include <memory> +#include <vector> +#include <linux/aspeed_peci_ioctl.h> + +#define PECI_DIMM_TEMP_REG 0x150 +#define DIMM_DEFAULT_VALUE 0x55 + +class DimmConfig { + public: + uint8_t chanId, bus, device, function, dimmnum; +}; + +#define MAX_BUFFER_SIZE 32 + +// TODO get this from config +auto GetDimmConfig(uint8_t dimm) { + uint8_t chan = dimm / 2; + assert(chan < 6); + + auto ret = std::make_unique<DimmConfig>(); + + ret->chanId = chan; + ret->dimmnum = 2; + ret->bus = 2; + switch (chan) { + case 0: + ret->device = 10; + ret->function = 2; + break; + case 1: + ret->device = 10; + ret->function = 6; + break; + case 2: + ret->device = 11; + ret->function = 2; + break; + case 3: + ret->device = 12; + ret->function = 2; + break; + case 4: + ret->device = 12; + ret->function = 6; + break; + case 5: + ret->device = 13; + ret->function = 2; + break; + default: + assert(0); + break; + } + + return ret; +} + +// returns read vector on success, empty vector on failure +auto peci_config_local(uint8_t u8target, uint8_t u8bus, uint8_t u8device, + uint8_t u8fcn, uint16_t u16reg, uint8_t u8readlen) { + auto msg = std::make_unique<peci_xfer_msg>(); + uint32_t u32Address; + int fd; + std::vector<uint8_t> ret; + + u32Address = u16reg; + u32Address |= u8fcn << 12; + u32Address |= u8device << 15; + u32Address |= u8bus << 20; + + msg->client_addr = u8target; + msg->tx_len = RDPCICFGLOCAL_WRITE_LEN; + msg->rx_len = RDPCICFGLOCAL_READ_LEN_BASE + u8readlen; + + msg->tx_buf[0] = RDPCICFGLOCAL_PECI_CMD; + msg->tx_buf[2] = u32Address & 0xFF; + msg->tx_buf[3] = (u32Address >> 8) & 0xFF; + msg->tx_buf[4] = (u32Address >> 16) & 0xFF; + + fd = open("/dev/peci", O_RDWR | O_CLOEXEC); + if (fd >= 0) { + int success = ioctl(fd, PECI_IOC_XFER, msg.get()); + if (success == 0) { + if (DEV_PECI_CC_SUCCESS == msg->rx_buf[0]) { + ret.resize(RDPCICFGLOCAL_READ_LEN_BASE + u8readlen - 1); + memcpy(ret.data(), &(msg->rx_buf[1]), ret.size()); + } + } + close(fd); + } + return ret; +} + +int main(int argc, char** argv) { + if (argc != 3) { + std::cout << argv[0] << " requires 2 arguments: CPUNum, DimmNum.\n"; + return -1; + } + uint8_t cpunum = atoi(argv[1]); + if (cpunum > 3) { + std::cout << cpunum << " greater than cpu max of 3.\n"; + return -1; + } + uint8_t dimmnum = atoi(argv[2]); + if (dimmnum > 11) { + std::cout << dimmnum << " greater than dimm max of 11.\n"; + return -1; + } + + auto dimm_config = GetDimmConfig(dimmnum); + + uint8_t dimmSelect = dimmnum % 2; // dimm 0 or 1 for each config + + auto val = peci_config_local(PECI_BASE_ADDR + cpunum, dimm_config->bus, + dimm_config->device, dimm_config->function, + PECI_DIMM_TEMP_REG + dimmSelect * 4, 4); + + if (!val.size()) { + std::cout << "Peci Error\n"; + return -1; + } + + // TODO dimm offsets needed? + + if (val[0] == 0) + std::cout << "Dimm " << unsigned(dimmnum) << " CPU " << unsigned(cpunum) + << " not populated.\n"; + else if(val[0] == DIMM_DEFAULT_VALUE) + std::cout << "Dimm " << unsigned(dimmnum) << " CPU " << unsigned(cpunum) + << " in illegal state.\n"; + else + std::cout << unsigned(val[0]) << " degrees C.\n"; +} diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/intel-signed-image/files/genimage-si.ini b/meta-openbmc-mods/meta-common/recipes-utilities/intel-signed-image/files/genimage-si.ini new file mode 100644 index 000000000..619570dbb --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/intel-signed-image/files/genimage-si.ini @@ -0,0 +1,277 @@ +; +; Copyright 2015 Intel Corporation. +; +; The source code, information and material ("Material") contained herein is +; owned by Intel Corporation or its suppliers or licensors, and title to such +; Material remains with Intel Corporation or its suppliers or licensors. The +; Material contains proprietary information of Intel or its suppliers and +; licensors. The Material is protected by worldwide copyright laws and treaty +; provisions. No part of the Material may be used, copied, reproduced, +; modified, published, uploaded, posted, transmitted, distributed or disclosed +; in any way without Intel's prior express written permission. No license under +; any patent, copyright or other intellectual property rights in the Material +; is granted to or conferred upon you, either expressly, by implication, +; inducement, estoppel or otherwise. Any license under such intellectual +; property rights must be express and approved by Intel in writing. + +; +; This file is similar to the config.genimage2 file of previous BMC +; generations but it is not generated. It contains all of the information +; to generate the signed-image variant of the signtool config file. +; + +[GLOBAL] +Major = 0 +Minor = 72 +Output = update.bin +Alloc = 33792K ; 0x2100000 +BlockSize = 64K +Type = 0xffffffff ; generate top level composite image +Locate = 0 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Factory ROM file generation +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +RomOutput = rom-a-only.ima +RomAlloc = 33M +KeyRegion = Certificate +NewKeyRegion = ReplacementCertificate +SecVersion = 0 + + + +; RecoveryOnly items should be place first in the image + +[FRISBEE_ROM] +Major = 1 +Minor = 5 +Type = ROBL +File = frisbee.bin +Locate = 0x00000000 +Alloc = 240K +Compress = 0 +SecVersion = 1 +Unsigned = 1 + +[Certificate] +Locate = 0x3c000 +Type = CERT +Alloc = 8K +Fill = 0xff +Unsigned = 1 +ROMOnly = 1 +SecVersion = 0 + +;[FWPRODUCTID] +;Major = 1 +;Minor = 0 +;Type = FWID +;File = fwproductid.bin +;Locate = 0x0003f000 +;ROMOnly = 1 +;SecVersion = 0 + +[FRISBEE_UPD] +Major = 1 +Minor = 5 +Type = RWBL +File = frisbee.bin +Locate = 0x040000 +Alloc = 256K +Compress = 0 +SecVersion = 1 +IndivSign = 1 +SigOffset = 0x03e000 + +[U-Boot] +Major = 1 +Minor = 5 +Type = UBT\x00 +File = u-boot.bin +Alloc = 256K +Compress = 0 +SecVersion = 1 +IndivSign = 1 +SigOffset = 0x03e000 + +; Linux OS Image +[OSIMAGE] +Major = 1 +Minor = 1 +Type = LKNL +File = uImage +SecVersion = 1 + +[DTB] +Major = 1 +Minor = 1 +Type = DTB\x00 +File = uImage-aspeed-bmc-intel-purley.dtb +SecVersion = 0 + +; Root File System +[ROOT] +Major = 1 +Minor = 1 +Type = RtFS +File = image-rofs.squashfs-xz.u-boot +Load = 0x83000000 +SecVersion = 1 +; +; WWW File System in CRAMFS. +;[WWW] +;Major = 1 +;Minor = 1 +;Type = WWW\x00 +;File = webfs.bin +;BlockDev = 1 +;SecVersion = 1 + +; Replacement certificate for re-keying the BMC +; Will only be added to image if -rk is specified +[ReplacementCertificate] +Major = 1 +Minor = 1 +Type = CRT0 +Alloc = 4K +SecVersion = 0 +Compress = 0 +IndivSign = 1 +Unbound = 1 +Fill = 0xff +SigOffset = 0x800 + +; Manifest goes here +; This gets some special treatment (this needs to match the location +; that Frisbee thinks the manifest is at or it won't boot) +[Manifest] +Major = 0 +Minor = 0 +Type = MFST +Alloc = 4K +Locate = 0x1bff000 +Fill = 0xff +SecVersion = 0 + +; +; NV File System in JFFS2, but it is blank in the ROM version +; and filled in with defaults from the rootfs +[PARAMS] +Major = 1 +Minor = 1 +Type = CONF +Alloc = 4096K +Locate = 0x1c00000 +ROMOnly = 1 +BlockDev = 1 +SecVersion = 1 +Unsigned = 1 + +; notice that these sections have no file +; and are marked as ROMOnly. This forces them +; into the allocation so we don't get overlapping +; sections, but does not actually put anything into +; the rom at build time. + +[UBootEnv] +Major = 1 +Minor = 0 +Type = UENV +; File = ; no file makes this a placeholder +Locate = 0x2000000 +Alloc = 64K +BlockDev = 1 +ROMOnly = 1 +Unsigned = 1 +SecVersion = 0 + +[FRUData] +Major = 1 +Minor = 0 +Type = FRU\x00 +Alloc = 64K +Locate = 0x2010000 +ROMOnly = 1 +Unsigned = 1 +SecVersion = 0 + +[SEL] +Major = 1 +Minor = 0 +Type = SEL\x00 +Alloc = 512K +Locate = 0x2020000 +BlockDev = 1 +ROMOnly = 1 +Unsigned = 1 +SecVersion = 0 + +; NV filesystem that survives reset mfg defaults. +; OEM Web customization goes here. +[PersistentNV] +Major = 1 +Minor = 0 +Type = PNV\x00 +Alloc = 2048K +Locate = 0x20a0000 +BlockDev = 1 +ROMOnly = 1 +Unsigned = 1 +SecVersion = 0 + +[RubixLog] +Major = 1 +Minor = 0 +Type = BTLG +Alloc = 4K +Locate = 0x23fe000 +BlockDev = 0 +ROMOnly = 1 +Unsigned = 1 +SecVersion = 0 + +[BootPointer] +Major = 1 +Minor = 0 +Type = BPTR +Alloc = 4K +Locate = 0x23ff000 +BlockDev = 0 +ROMOnly = 1 +Unsigned = 1 +SecVersion = 0 + +; +; Example Section with all possible fields with their default +; values, unless the field is specified mandatory +; +;[EXAMPLE] +;Major = 0 ; Major number of module +;Minor = 0 ; Minor number of module +;Type = TYPE ; four bytes hopefully human readable, +; ; use c-style escapes if non-ascii, \x23\x10\x00\xf3 +; ; or use a number 0xf3001023 +;Alloc = X ; Maximum memory allocated; X = roundup +;File = name ; File containing the module +; ; If Alloc is specified, but no File, a blank +; ; header will be created (only useful for ROMOnly) +;Locate = addr ; Location in Flash; MANDATORY +;ROMOnly = 1 ; if ROMOnly is set and non-zero, this section +; ; will only be present in the ROM image, not the updater image +;RecoveryOnly = 1 ; if RecoveryOnly is set and non-zero, this section +; ; will only be present in the recovery image, not the active image +;BlockDev = 1 ; this will make the signed image code export this +; ; image as a mtd block device at runtime +;Unsigned = 1 ; Do not include this section in signatures (NV data) +;Compress = 1 ; compress image contents (useful for non-compressed items) +;SecVersion = X ; a 16-bit security revision to enforce no downgrades +;IndivSign = 1 ; section is individually signed; do not include in full signature +;Unbound = 1 ; by default, individually signed sections are part of the +; ; full signature too. This makes them independent +;SigOffset = X ; offset within individually-signed section to place signature +;Fill = xx ; fill with pattern xx instead of a file's contents +; +; Note: Numeric values can be represented either by decimal or a +; hexadecimal (Prefixed by 0x) +; Numeric values can either end with K or M to specify in +; KiloBytes or MegaBytes +; diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/io-app/files/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-utilities/io-app/files/CMakeLists.txt new file mode 100644 index 000000000..0c98160b9 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/io-app/files/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR) +project(io-app C) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +add_executable(io io-app.c) +install (TARGETS io DESTINATION bin) + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/io-app/files/io-app.c b/meta-openbmc-mods/meta-common/recipes-utilities/io-app/files/io-app.c new file mode 100644 index 000000000..a5ffabd88 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/io-app/files/io-app.c @@ -0,0 +1,665 @@ +/* +// Copyright (c) 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Abstract: map io region to read or write to HW registers +// +*/ + +#define _GNU_SOURCE +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <termios.h> +#include <ctype.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/mman.h> + +static int quiet = 0; + +static int dump(unsigned long phys, void *addr, size_t len) +{ + const uint32_t *ubuf = addr; + unsigned int wd = 0, l, iv; + unsigned char *h, *a; + char line[256]; + static const char itoh[] = "0123456789abcdef"; + + /* 0 1 2 3 4 5 6 + * 0123456789012345678901234567890123456789012345678901234567890123456789 + * 00000000: 00000000 00000000 00000000 00000000 ................ + */ + while (wd < len) + { + memset(line, ' ', sizeof(line)); + sprintf(line, "%08lx: ", phys + (wd*sizeof(uint32_t))); + a = (unsigned char*)&line[47]; + h = (unsigned char*)&line[10]; + for (l=0; l<4 && (l+wd)<len; l++) + { + uint32_t v = *ubuf++; + for (iv=0; iv<sizeof(v); iv++) + { + uint8_t b = v >> (8*((sizeof(v)-1)-iv)); + *h++ = itoh[b>>4]; + *h++ = itoh[b&0xf]; + if (isprint(b)) + *a++ = (char)b; + else + *a++ = '.'; + } + *h++ = ' '; + } + *a++ = '\n'; + *a = 0; + wd += l; + fputs(line, stdout); + } + return wd; +} + + +struct mapping +{ + unsigned long phys; + void *virt; + size_t len; +}; + +static struct mapping *maps = NULL; +static int nr_maps = 0; + +static void unmap_all(void) +{ + int i; + for (i=0; i<nr_maps; i++) + { + if (maps[i].virt) + munmap(maps[i].virt, maps[i].len); + maps[i].virt = NULL; + } +} + +int add_a_map(unsigned long phys, void *virt, size_t len) +{ + void *new_maps; + new_maps = realloc(maps, (nr_maps + 1) * sizeof(struct mapping)); + if (!new_maps) + { + unmap_all(); + munmap(virt, len); + } + else + { + maps = new_maps; + maps[nr_maps].phys = phys; + maps[nr_maps].virt = virt; + maps[nr_maps].len = len; + nr_maps++; + return 0; + } + return -1; +} + +static void *map_fd(int fd, off_t offset, size_t len, int mode, int flags) +{ + void *mapped_at; + unsigned long phys = -1; + + if (offset != -1) + phys = offset; + else + offset = 0; + + mapped_at = mmap(NULL, len, mode, flags, fd, offset); + if (mapped_at != MAP_FAILED) + { + if (add_a_map(phys, mapped_at, len) != 0) + { + mapped_at = MAP_FAILED; + } + } + + return mapped_at; +} + +static void *map_file(const char *fname, size_t *len, int mode, int flags) +{ + int fd; + struct stat sb; + void *ptr; + int fmode; + + if (mode & PROT_WRITE) + fmode = O_RDWR; + else + fmode = O_RDONLY; + + fd = open(fname, fmode); + if (fd < 0) + return MAP_FAILED; + + if (*len == 0) + { + fstat(fd, &sb); + *len = sb.st_size; + } + ptr = map_fd(fd, -1, *len, mode, flags); + close(fd); + return ptr; +} + +static int load_maps(const char *cmap_str, size_t mlen) +{ + char *tmp_sa = NULL, *tmp_sl = NULL, *endptr = NULL; + const void *mapped = NULL; + int ret = 0; + const char *delim = "\r\n\t ,"; + unsigned long addr; + size_t len; + char *map_str = NULL, *paddr = NULL, *plen = NULL; + int fd; + + fd = open("/dev/mem", O_RDWR); + if (fd < 0) + { + return -1; + } + + len = strlen(cmap_str); + map_str = (char *)malloc(len + 1); + if (!map_str) + { + close(fd); + return -1; + } + strncpy(map_str, cmap_str, len); + map_str[len] = '\0'; + paddr = strtok_r(map_str, delim, &tmp_sa); + while (paddr) + { + /* find the next comma or newline */ + if (!strtok_r(paddr, ":", &tmp_sl)) + { + fprintf(stderr, "malformed map string '%s'\n", paddr); + goto _loop; + } + plen = strtok_r(NULL, ":", &tmp_sl); + if (!plen) + { + goto _loop; + } + addr = strtoul(paddr, &endptr, 16); + if (*endptr) + { + fprintf(stderr, "Failed to parse address from '%s'\n", paddr); + ret = -1; + break; + } + len = strtoul(plen, &endptr, 16); + if (*endptr) + { + fprintf(stderr, "Failed to parse len from '%s'\n", plen); + ret = -1; + break; + } + if (MAP_FAILED == (mapped = map_fd(fd, addr, len, PROT_READ|PROT_WRITE, MAP_SHARED))) + { + fprintf(stderr, "Failed to map %lx +%x\n", addr, len); + ret = -1; + break; + } + if (!quiet) + printf("added map: %p (%lx..%lx)\n", mapped, addr, addr+len); +_loop: + paddr = strtok_r(NULL, delim, &tmp_sa); + } + free(map_str); + close(fd); + return ret; +} + +int md(unsigned long addr, uint32_t unused, size_t len) +{ + int i, j; + + (void)unused; + for (i=0; i<nr_maps; i++) + { + if (-1 == maps[i].phys) + continue; + if (maps[i].phys <= addr && + (addr + len * sizeof(uint32_t)) < (maps[i].phys + maps[i].len)) + { + uint32_t *buf, *pv; + + buf = (uint32_t *)malloc(len*sizeof(uint32_t)); + if (!buf) + return 1; + pv = (uint32_t *)(maps[i].virt + (addr - maps[i].phys)); + for (j=0; j<len; j++) + buf[j] = *pv++; + + dump(addr, buf, len); + free(buf); + return 0; + } + } + fprintf(stderr, "%lx +%x not in mapped memory\n", addr, len); + return 1; +} + +int mw(unsigned long addr, uint32_t val, size_t len) +{ + int i, j; + + for (i=0; i<nr_maps; i++) + { + if (-1 == maps[i].phys) + continue; + if (maps[i].phys <= addr && + (addr + len * sizeof(uint32_t)) < (maps[i].phys + maps[i].len)) + { + for (j=0; j<len; j++) + { + *((uint32_t*)(maps[i].virt + (addr - maps[i].phys))) = val; + } + return 0; + } + } + fprintf(stderr, "%lx +%x not in mapped memory\n", addr, len); + return 1; +} + +char *readline(char *buf, size_t len, FILE *f) +{ + int raw = 0; + size_t br = 0; + struct termios tios, orig_tios; + + if (!quiet) + { + /* put terminal in raw mode to get unbuffered io */ + if (tcgetattr(fileno(f), &orig_tios) == 0) + { + tios = orig_tios; + tios.c_iflag |= IGNPAR; + tios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); + tios.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL | IEXTEN); + tios.c_oflag &= ~OPOST; + tios.c_cc[VMIN] = 1; + tios.c_cc[VTIME] = 0; + tcsetattr(fileno(f), TCSADRAIN, &tios); + raw = 1; + } + } + if (!raw) + { + return fgets(buf, len, f); + } + /* read in bytes one at a time and echo them */ + while (br < (len-1)) + { + int c = fgetc(f); + switch (c) + { + case 3: /* ^C */ + br = 0; + c = '\n'; + break; + case 4: + br = 0; + c = -1; + break; + case '\b': + if (br > 0) + { + fputs("\b \b", stdout); + br--; + } + break; + case '\r': + case '\n': + fputs("\r\n", stdout); + buf[br++] = '\n'; + break; + case ' '...'~': + fputc(c, stdout); + buf[br++] = c; + break; + break; + default: + break; + } + if (c == -1) + { + if (br == 0) + buf = NULL; + break; + } + if (c == '\r' || c == '\n') + break; + } + if (buf) + buf[br] = 0; + + tcsetattr(fileno(f), TCSADRAIN, &orig_tios); + return buf; +} + +#define MAX_LINE_LEN 4096 +int io(void) +{ + const char delim1[] = "\r\n;"; + const char delim2[] = "\t "; + char line[MAX_LINE_LEN]; + char *command, *cmd, *paddr, *pval, *plen, *endptr; + char *tmp_s1, *tmp_s2; + unsigned long addr; + uint32_t val; + size_t len; + int (*fn)(unsigned long, uint32_t, size_t); + + if (!quiet) + fputs("> ", stdout); + while (readline(line, MAX_LINE_LEN, stdin) != NULL && *line) + { + /* read next line or next command (up to newline or ';') */ + command = strtok_r(line, delim1, &tmp_s1); + if (!command || strlen(command) == 0) + goto _command_loop; + while (NULL != command && strlen(command) > 0) + { + cmd = strtok_r(command, delim2, &tmp_s2); + if (!cmd) + goto _cmd_err; + if (cmd[0] == 'q' || cmd[0] == 'Q') + return 0; + paddr = strtok_r(NULL, delim2, &tmp_s2); + if (!paddr) + goto _cmd_err; + addr = strtoul(paddr, &endptr, 16); + if (*endptr) + goto _cmd_err; + fn = NULL; + if (strncmp(cmd, "mw", 3) == 0) + { + fn = mw; + pval = strtok_r(NULL, delim2, &tmp_s2); + if (!pval) + goto _cmd_err; + val = strtoul(pval, &endptr, 16); + if (*endptr) + goto _cmd_err; + len = 1; + } + else if (strncmp(cmd, "md", 3) == 0) + { + fn = md; + len = 0x40; + val = 0; + } + else + { + goto _cmd_err; + } + plen = strtok_r(NULL, delim2, &tmp_s2); + if (plen) + { + len = strtoul(plen, &endptr, 16); + if (*endptr) + goto _cmd_err; + } + + if (fn) + fn(addr, val, len); + + command = strtok_r(NULL, delim1, &tmp_s1); + } +_command_loop: + if (!quiet) + fputs("> ", stderr); + continue; +_cmd_err: + fprintf(stderr, "md addr [len]\nmw addr val [len]\n" + "q[uit] | ctrl-d | ctrl-c to exit\n"); + if (!quiet) + fputs("> ", stderr); + } + return 0; +} + +typedef enum +{ + CPU_NONE = 0, + CPU_PILOT3, + CPU_PILOT4, + CPU_AST2500, + CPU_MAX, +} CPU_TYPE; + +static CPU_TYPE probe_cpu(void) +{ + FILE *f; + char cpuinfo[128]; + static CPU_TYPE this_cpu = CPU_NONE; + + if (CPU_NONE == this_cpu) + { + f = fopen("/sys/firmware/devicetree/base/compatible", "r"); + if (f) { + int br = fread(cpuinfo, 1, sizeof(cpuinfo)-1, f); + if (br > 0) { + cpuinfo[br] = 0; + char *v = cpuinfo; + while (v < (cpuinfo + sizeof(cpuinfo)) && *v) { + if (strncmp("aspeed,ast2500", v, 15) == 0) + { + if (!quiet) + fprintf(stderr, "AST2500\n"); + this_cpu = CPU_AST2500; + } + v += 1 + strnlen(v, sizeof(cpuinfo) - (v - cpuinfo)); + } + } + fclose(f); + } + } + if (CPU_NONE == this_cpu) + { + const char delim[] = "\r\n\t :"; + char *tmp_s; + f = fopen("/proc/cpuinfo", "r"); + if (f != NULL) { + while (fgets(cpuinfo, sizeof(cpuinfo), f)) + { + strtok_r(cpuinfo, delim, &tmp_s); + if (strncmp("Hardware", cpuinfo, 9) == 0) + { + char *v = strtok_r(NULL, delim, &tmp_s); + if (v) + { + if (strncmp("AST2500", v, 8) == 0) + { + if (!quiet) + fprintf(stderr, "AST2500\n"); + this_cpu = CPU_AST2500; + } + else if (strncmp("ASpeed SoC", v, 11) == 0) + { + if (!quiet) + fprintf(stderr, "Found ASpeed SoC\n"); + this_cpu = CPU_AST2500; + } + else if (strncmp("ServerEngines PILOT3", v, 21) == 0) + { + if (!quiet) + fprintf(stderr, "Found PILOT3\n"); + this_cpu = CPU_PILOT3; + } + } + break; + } + } + fclose(f); + } + } + return this_cpu; +} + +static const char *probe_cpu_for_map(void) +{ + switch (probe_cpu()) + { + case CPU_PILOT3: + return "0:2000000,10000000:8000,40000000:43b000"; + case CPU_AST2500: + return "0:4000000,1e600000:1a0000,20000000:4000000"; + default: + return ""; + } +} + +static void usage(void) +{ + fprintf(stderr, + "Usage: io [-c config] [-m map]\n" + " md [-c config] [-m map] <addr> [len]\n" + " mw [-c config] [-m map] <addr> <val> [len]\n\n" + "With: -c config load mappings from file config\n" + " -m map load mappings from string map\n" + " addr, val, len are all hex numbers\n\n" + "When invoked as io, this will start a shell that will\n" + "allow the user to type in md and mw commands much like\n" + "the U-Boot environment. By default, it will map in all\n" + "the addresses for the known processor type.\n\n" + "map string is of the format addr:len[,addr2:len2...]\n" + "config file is of the same format as map string\n" + "but with each mapping on separate lines instead of\n" + "comma separated values\n" + ); + exit(1); +} + +#define shift if (++i >= argc) usage() + +int main(int argc, const char *argv[]) +{ + char *exe_full; + char *exe; + char *endptr; + int i, first_arg = 1; + const char *cfg_file = NULL; + const char *map_str = NULL; + size_t flen = 0; + size_t len = 0; + unsigned long addr; + uint32_t val; + int ret = 0; + + i = 1; + while (i < argc) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'm': + shift; + map_str = argv[i]; + break; + case 'c': + shift; + cfg_file = argv[i]; + break; + default: + usage(); + } + } + else + { + first_arg = i; + break; + } + } + + exe_full = strdup(argv[0]); + if (exe_full != NULL ) + exe = basename(exe_full); + else + return ret; + + if (strncmp(exe, "io", 3) != 0 || !isatty(fileno(stdin))) + quiet = 1; + + if (!map_str) + { + if (!cfg_file) + map_str = probe_cpu_for_map(); + else + map_str = map_file(cfg_file, &flen, PROT_READ, MAP_PRIVATE); + } + else + { + flen = strlen(map_str); + } + if (load_maps(map_str, flen) < 0) + { + fprintf(stderr, "failed to map regions: check map string or config file\n"); + goto _cleanup; + } + + if (strncmp(exe, "md", 3) == 0) + { + len = 0x40; + addr = strtoul(argv[first_arg], &endptr, 16); + if ((first_arg + 1) < argc) + { + len = strtoul(argv[first_arg + 1], &endptr, 16); + } + ret = md(addr, 0, len); + goto _cleanup; + } + + if (strncmp(exe, "mw", 3) == 0) + { + len = 1; + addr = strtoul(argv[first_arg], &endptr, 16); + if ((first_arg + 1) < argc) + { + val = strtoul(argv[first_arg + 1], &endptr, 16); + } + else + { + usage(); + } + if ((first_arg + 2) < argc) + { + len = strtoul(argv[first_arg + 2], &endptr, 16); + } + ret = mw(addr, val, len); + goto _cleanup; + } + + io(); + +_cleanup: + unmap_all(); + free(exe_full); + return ret; +} diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/io-app/io-app.bb b/meta-openbmc-mods/meta-common/recipes-utilities/io-app/io-app.bb new file mode 100644 index 000000000..5ac0f0a75 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/io-app/io-app.bb @@ -0,0 +1,17 @@ +SUMMARY = "IO App" +DESCRIPTION = "IO application for accessing memory-mapped IO regions on the BMC" + +inherit cmake + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM += "\ + file://io-app.c;beginline=2;endline=14;md5=639666a0bf40bb717b46b378297eeceb \ + " + +SRC_URI = "\ + file://CMakeLists.txt \ + file://io-app.c \ + " + +S = "${WORKDIR}" + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/CMakeLists.txt new file mode 100644 index 000000000..1cde22b49 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR) +project(lpc-cmds C) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +add_executable(lpc_cmds lpc_cmds.c) +install (TARGETS lpc_cmds DESTINATION bin) + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/lpc_cmds.c b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/lpc_cmds.c new file mode 100644 index 000000000..2cdf66d88 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/lpc_cmds.c @@ -0,0 +1,479 @@ +/* +// Copyright (c) 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +*/ + +#include <stdio.h> +#include <time.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +#include "lpc_drv.h" + +#define SIO_DEVICE_NAME "/dev/lpc-sio" +#define KCS_DEVICE_NAME "/dev/ipmi-kcs" +#define MAILBOX_DEVICE_NAME "/dev/aspeed-mbox" +#define SNOOP_DEVICE_NAME "/dev/aspeed-lpc-snoop" + +#define SNOOP_BUF_SIZE 4096 + +#define SUPPORT_KCS_ADDR_CMD 0 +#define SUPPORT_MAILBOX 1 +#define SUPPORT_SNOOP 1 + +/*********************************************************************************/ +static void ProcessKCSReq(int fd, unsigned char *pReq, int ReqLen, int NoPrint) +{ + int i; + unsigned char SendPkt[16]; + + if (!NoPrint) { + printf("\nKCS Request >>>>>>>>>>>>>>>>>>>>>>>>>>\n"); + for (i = 0; i < ReqLen; i++) + printf("%02X ", pReq[i]); + printf("\n======================================\n"); + } + + SendPkt[0] = pReq[0] | 0x04; + SendPkt[1] = pReq[1]; + SendPkt[2] = 0xC1; /* Always Invalid Command */ + + if (!NoPrint) { + printf("\nKCS Response <<<<<<<<<<<<<<<<<<<<<<<<<\n"); + for (i = 0; i < 3; i++) + printf("%02X ", SendPkt[i]); + printf("\n======================================\n"); + } + + write(fd, SendPkt, 3); +} + +static void KCSIfcTask(int KCSIfcIdx, int NoPrint) +{ + int fd; + int RecvPktLen; + char KCSDev[16]; + struct kcs_ioctl_data IOData; + unsigned char RecvPkt[512]; + + snprintf(KCSDev, sizeof(KCSDev), KCS_DEVICE_NAME"%d", KCSIfcIdx); + fd = open(KCSDev, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open KCS device: %s\n", KCSDev); + exit(1); + } + + IOData.cmd = KCS_FORCE_ABORT; + IOData.data = 0; + ioctl(fd, KCS_IOC_COMMAND, &IOData); + + while (1) { + RecvPktLen = read(fd, RecvPkt, sizeof(RecvPkt)); + if (RecvPktLen < 2) + continue; + + ProcessKCSReq(fd, RecvPkt, RecvPktLen, NoPrint); + } +} + +#if SUPPORT_KCS_ADDR_CMD +static void KCSIfcSetAddr(int KCSIfcIdx, unsigned int addr) +{ + int fd; + struct kcs_ioctl_data kcs_data; + char KCSDev[16]; + + snprintf(KCSDev, sizeof(KCSDev), KCS_DEVICE_NAME"%d", KCSIfcIdx); + fd = open(KCSDev, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open KCS device: %s\n", KCSDev); + exit(1); + } + + kcs_data.cmd = KCS_SET_ADDR; + kcs_data.data = addr; + if (ioctl(fd, KCS_IOC_COMMAND, &kcs_data) == 0) + printf("Set KCS%d addr to 0x%X successfully!\n", KCSIfcIdx + 1, addr); + + close(fd); +} + +static void KCSIfcGetAddr(int KCSIfcIdx) +{ + int fd; + struct kcs_ioctl_data kcs_data; + char KCSDev[16]; + + snprintf(KCSDev, sizeof(KCSDev), KCS_DEVICE_NAME"%d", KCSIfcIdx); + fd = open(KCSDev, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open KCS device: %s\n", KCSDev); + exit(1); + } + + kcs_data.cmd = KCS_GET_ADDR; + if (ioctl(fd, KCS_IOC_COMMAND, &kcs_data) == 0) + printf("KCS%d addr is : 0x%X!\n", KCSIfcIdx + 1, kcs_data.data); + + close(fd); +} +#endif + +/*********************************************************************************/ + +#if SUPPORT_SNOOP +static void ReadBiosPOSTCodes(unsigned int if_idx) +{ + char snoop_dev[32]; + int fd; + int i; + unsigned char buf[SNOOP_BUF_SIZE]; + int len; + + snprintf(snoop_dev, sizeof(snoop_dev), SNOOP_DEVICE_NAME"%d", if_idx); + fd = open(snoop_dev, O_RDONLY | O_NONBLOCK | O_CLOEXEC); + if (fd < 0) { + printf("Error open %s !\n", snoop_dev); + return; + } + + len = read(fd, &buf, sizeof(buf)); + + if (len == 0 || errno == EAGAIN) { + printf("No BIOS POST Codes Found!\n"); + goto out; + } else if (len < 0) { + printf("Failed to read the POST Codes! (%s)\n", + strerror(errno)); + goto out; + } + + printf("BIOS POST Codes in Hex (%d entries):\n", len); + + for (i = 0; i < len; i++) + printf(" %d: %02X\n", i, buf[i]); + + printf("\n"); + +out: + close(fd); +} +#endif + +/*********************************************************************************/ + +static void SIOGetACPIState(unsigned short changed) +{ + int fd; + struct sio_ioctl_data sio_data; + + fd = open(SIO_DEVICE_NAME, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open %s\n", SIO_DEVICE_NAME); + exit(1); + } + + sio_data.sio_cmd = SIO_GET_ACPI_STATE; + sio_data.param = changed; + + if (ioctl(fd, SIO_IOC_COMMAND, &sio_data) == 0) { + if (changed) + printf("ACPI SLP state is %s!\n", + sio_data.param != 0 ? "Changed" : "Same"); + + if (sio_data.data == ACPI_STATE_S12) + printf("ACPI SLP state --> SLP_12\n"); + else if (sio_data.data == ACPI_STATE_S3I) + printf("ACPI SLP state --> SLP_3I\n"); + else if (sio_data.data == ACPI_STATE_S45) + printf("ACPI SLP state --> SLP_45\n"); + } + + close(fd); +} + +static void SIOGetPWRGDStatus(unsigned short changed) +{ + int fd; + struct sio_ioctl_data sio_data; + + fd = open(SIO_DEVICE_NAME, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open %s\n", SIO_DEVICE_NAME); + exit(1); + } + + sio_data.sio_cmd = SIO_GET_PWRGD_STATUS; + sio_data.param = changed; + + if (ioctl(fd, SIO_IOC_COMMAND, &sio_data) == 0) { + if (changed) + printf("PWRGD status : %s\n", + sio_data.param != 0 ? "Changed" : "Same"); + + printf("PWRGD status value :%u\n", sio_data.data); + } + + close(fd); +} + +static void SIOGetONCTLStatus(void) +{ + int fd; + struct sio_ioctl_data sio_data; + + fd = open(SIO_DEVICE_NAME, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open %s\n", SIO_DEVICE_NAME); + exit(1); + } + + sio_data.sio_cmd = SIO_GET_ONCTL_STATUS; + + if (ioctl(fd, SIO_IOC_COMMAND, &sio_data) == 0) + printf("ONCTL status value :%u\n", sio_data.data); + + close(fd); +} + +static void SIOSetONCTLGPIO(unsigned short enable, unsigned int value) +{ + int fd; + struct sio_ioctl_data sio_data; + + fd = open(SIO_DEVICE_NAME, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open %s\n", SIO_DEVICE_NAME); + exit(1); + } + + sio_data.sio_cmd = SIO_SET_ONCTL_GPIO; + sio_data.param = enable; + sio_data.data = value; + + if (ioctl(fd, SIO_IOC_COMMAND, &sio_data) == 0) + printf("ONCTL GPIO mode setting is Done!\n"); + + close(fd); +} + +static void SIOGetPWRBTNOverride(unsigned short clear) +{ + int fd; + struct sio_ioctl_data sio_data; + + fd = open(SIO_DEVICE_NAME, O_RDWR | O_CLOEXEC); + if (fd < 0) { + printf("Error open %s\n", SIO_DEVICE_NAME); + exit(1); + } + + sio_data.sio_cmd = SIO_GET_PWRBTN_OVERRIDE; + sio_data.param = clear; + + if (ioctl(fd, SIO_IOC_COMMAND, &sio_data) == 0) + printf("PWRBTN Override status : %u\n", sio_data.data); + + close(fd); +} + +/*********************************************************************************/ + +#if SUPPORT_MAILBOX +static void MailBoxRead(int num) +{ + int fd; + int len; + uint8_t data; + + fd = open(MAILBOX_DEVICE_NAME, O_RDWR | O_NONBLOCK | O_CLOEXEC); + if (fd < 0) { + printf("Error open mailbox\n"); + exit(1); + } + + len = pread(fd, &data, 1, num); + if (len == 0 || errno == EAGAIN) { + printf("No mailbox message found!\n"); + goto out; + } else if (len < 0) { + printf("Error reading from mailbox%d! (%s)\n", num, + strerror(errno)); + goto out; + } + + printf("MailBox%d read value : 0x%02X\n", num, data); + +out: + close(fd); +} + +static void MailBoxWrite(int num, uint8_t value) +{ + int fd; + ssize_t rc; + + fd = open(MAILBOX_DEVICE_NAME, O_RDWR | O_NONBLOCK | O_CLOEXEC); + if (fd < 0) { + printf("Error open mailbox\n"); + exit(1); + } + + rc = pwrite(fd, &value, 1, num); + + if (rc == 1) + printf("MailBox%d write value : 0x%02X done!\n", num, value); + else + printf("Error writing to mailbox%d, rc: %d\n", num, rc); + + close(fd); +} +#endif + +/*********************************************************************************/ + +static void usage(void) +{ + printf("Usage:\n" + "\tlpc_cmds sio get_acpi_state\n" + "\tlpc_cmds sio get_acpi_changed\n" + "\tlpc_cmds sio get_pwrgd_status\n" + "\tlpc_cmds sio get_pwrgd_changed\n" + "\tlpc_cmds sio get_onctl_status\n" + "\tlpc_cmds sio set_onctl_gpio_disable\n" + "\tlpc_cmds sio set_onctl_gpio_high\n" + "\tlpc_cmds sio set_onctl_gpio_low\n" + "\tlpc_cmds sio get_pwrbtn_override_status\n" + "\tlpc_cmds sio get_pwrbtn_override_status_clear\n" + "\n" +#if SUPPORT_KCS_ADDR_CMD + "\tlpc_cmds kcs [1 ~ 4] (getaddr / setaddr / quiet)\n" +#else + "\tlpc_cmds kcs [1 ~ 4] (quiet)\n" +#endif +#if SUPPORT_MAILBOX + "\n" + "\tlpc_cmds mailbox read (0 ~ 15)\n" + "\tlpc_cmds mailbox write (0 ~ 15) (0x00 ~ 0xFF)\n" +#endif +#if SUPPORT_SNOOP + "\n" + "\tlpc_cmds snoop [0 ~ 1] read\n" +#endif + ); + + exit(-1); +} + +int main(int argc, char** argv) +{ + char *cmd; + + if (argc < 2) + usage(); + + cmd = argv[1]; + + if (strcmp(cmd, "sio") == 0) { + if (argc < 3) + usage(); + + if (strcmp(argv[2], "get_acpi_state") == 0) + SIOGetACPIState(0); + else if (strcmp(argv[2], "get_acpi_changed") == 0) + SIOGetACPIState(1); + else if (strcmp(argv[2], "get_pwrgd_status") == 0) + SIOGetPWRGDStatus(0); + else if (strcmp(argv[2], "get_pwrgd_changed") == 0) + SIOGetPWRGDStatus(1); + else if (strcmp(argv[2], "get_onctl_status") == 0) + SIOGetONCTLStatus(); + else if (strcmp(argv[2], "set_onctl_gpio_disable") == 0) + SIOSetONCTLGPIO(0, 0); + else if (strcmp(argv[2], "set_onctl_gpio_high") == 0) + SIOSetONCTLGPIO(1, 1); + else if (strcmp(argv[2], "set_onctl_gpio_low") == 0) + SIOSetONCTLGPIO(1, 0); + else if (strcmp(argv[2], "get_pwrbtn_override_status") == 0) + SIOGetPWRBTNOverride(0); + else if (strcmp(argv[2], "get_pwrbtn_override_status_clear") == 0) + SIOGetPWRBTNOverride(1); + } else if (strcmp(cmd, "kcs") == 0) { + int ifc; + + if (argc < 3) + usage(); + + ifc = atoi(argv[2]); + if (ifc < 1 || ifc > 4) /* ipmi-kcs1 ~ ipmi-kcs4 */ + usage(); + + if (argc == 3) + KCSIfcTask(ifc, 0); + else if (argc == 4 && strcmp(argv[3], "quiet") == 0) + KCSIfcTask(ifc, 1); +#if SUPPORT_KCS_ADDR_CMD + else if (argc == 4 && strcmp(argv[3], "getaddr") == 0) + KCSIfcGetAddr(ifc); + else if (argc == 5 && strcmp(argv[3], "setaddr") == 0) + KCSIfcSetAddr(ifc, strtoul(argv[4], NULL, 16)); +#endif +#if SUPPORT_MAILBOX + } else if (strcmp(cmd, "mailbox") == 0) { + if (argc < 4) + usage(); + + if (strcmp(argv[2], "read") == 0) { + MailBoxRead(atoi(argv[3])); + } else { + if (argc < 5) + usage(); + MailBoxWrite(atoi(argv[3]), strtoul(argv[4], NULL, 16)); + } +#endif +#if SUPPORT_SNOOP + } else if (strcmp(cmd, "snoop") == 0) { + int ifc; + + if (argc < 3) + usage(); + + ifc = atoi(argv[2]); + if (ifc < 0 || ifc > 1) /* snoop0 ~ snoop1 */ + usage(); + + if (strcmp(argv[3], "read") == 0) + ReadBiosPOSTCodes(ifc); + else + usage(); +#endif + } + + return 0; +} + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/lpc_drv.h b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/lpc_drv.h new file mode 100644 index 000000000..56c79d1c1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/files/lpc_drv.h @@ -0,0 +1,78 @@ +/* +// Copyright (c) 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +*/ + +#ifndef __LPC_DRV_H__ +#define __LPC_DRV_H__ + +#define LPC_DEV_MAJOR 250 +#define LPC_DEV_MINOR 0 + +/***********************************************************************************/ + +enum KCS_CMD { + KCS_SET_ADDR = 0, + KCS_GET_ADDR, + KCS_SMS_ATN, + KCS_FORCE_ABORT, +}; + +struct kcs_ioctl_data { + unsigned int cmd; + unsigned int data; +}; + +#define KCS_IOC_BASE 'K' +#define KCS_IOC_COMMAND _IOWR(KCS_IOC_BASE, 1, struct kcs_ioctl_data) + +/***********************************************************************************/ + +enum ACPI_SLP_STATE { + ACPI_STATE_S12 = 1, + ACPI_STATE_S3I, + ACPI_STATE_S45 +}; + +/* SWC & ACPI for SuperIO IOCTL */ +enum SIO_CMD { + SIO_GET_ACPI_STATE = 0, + SIO_GET_PWRGD_STATUS, + SIO_GET_ONCTL_STATUS, + SIO_SET_ONCTL_GPIO, + SIO_GET_PWRBTN_OVERRIDE, + SIO_GET_PFAIL_STATUS, /* Start from AC Loss */ + + SIO_MAX_CMD +}; + +struct sio_ioctl_data { + unsigned short sio_cmd; + unsigned short param; + unsigned int data; +}; + +#define SIO_IOC_BASE 'P' +#define SIO_IOC_COMMAND _IOWR(SIO_IOC_BASE, 1, struct sio_ioctl_data) + +/***********************************************************************************/ + +#define MAX_MAILBOX_NUM 16 + +/***********************************************************************************/ + +#endif + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/lpc-cmds.bb b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/lpc-cmds.bb new file mode 100644 index 000000000..2fbb1eb2f --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/lpc-cmds/lpc-cmds.bb @@ -0,0 +1,16 @@ +SUMMARY = "LPC tools" +DESCRIPTION = "command tool for LPC interface test on the BMC" + +inherit cmake + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${PHOSPHORBASE}/LICENSE;md5=19407077e42b1ba3d653da313f1f5b4e" + +SRC_URI = "\ + file://CMakeLists.txt \ + file://lpc_drv.h \ + file://lpc_cmds.c \ + " + +S = "${WORKDIR}" + diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/peci-hwmon-test/files/peci-hwmon-test.py b/meta-openbmc-mods/meta-common/recipes-utilities/peci-hwmon-test/files/peci-hwmon-test.py new file mode 100755 index 000000000..977fcd3a0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/peci-hwmon-test/files/peci-hwmon-test.py @@ -0,0 +1,144 @@ +#!/usr/bin/python + +import glob +import os +import sys +import time + +if len(sys.argv) == 1: + cpuno = 0 +else: + cpuno = int(sys.argv[1]) + +hwmon_path = '/sys/class/hwmon' +cputemp_name_match = '{}{}'.format('peci_cputemp.cpu', cpuno) +dimmtemp_name_match = '{}{}'.format('peci_dimmtemp.cpu', cpuno) + +dimmtemp_path = '' + +os.chdir(hwmon_path) + +for dirpath, dirnames, files in os.walk(hwmon_path): + for d in dirnames: + try: + with open('{}/{}'.format(d, 'name')) as f: + hwmon_name = f.read().strip() + if hwmon_name == cputemp_name_match: + cputemp_path = os.path.abspath(d) + cputemp_name = hwmon_name + elif hwmon_name == dimmtemp_name_match: + dimmtemp_path = os.path.abspath(d) + dimmtemp_name = hwmon_name + except: + continue + +if not cputemp_path: + print "Can't find the " + cputemp_name_match + quit() + +try: + while True: + os.system('clear') + os.chdir(cputemp_path) + + print '{}/{}: {}'.format(hwmon_path, cputemp_path, cputemp_name) + if dimmtemp_path: + print '{}/{}: {}'.format(hwmon_path, dimmtemp_path, dimmtemp_name) + + print + print 'Package temperature' + for input in glob.glob('temp[1-5]_input'): + try: + with open(input) as f: + val = f.read().strip() + except IOError: + val = 0 + try: + with open(input.replace('input', 'label')) as l: + name = l.read().strip() + except IOError: + name = '' + print '{:11s}:{:3d}.{:03d}'.format( + name, (int(val) / 1000), (int(val) % 1000)) + + print + print 'Core temperature' + count = 0 + for input in glob.glob('temp[!1-5]_input'): + try: + with open(input) as f: + val = f.read().strip() + except IOError: + val = 0 + try: + with open(input.replace('input', 'label')) as l: + name = l.read().strip() + except IOError: + name = '' + print ('{:9s}:{:3d}.{:03d}'.format( + name, (int(val) / 1000), (int(val) % 1000))), + count += 1 + if count % 3 == 0: + print + else: + print ('\t'), + for input in glob.glob('temp??_input'): + try: + with open(input) as f: + val = f.read().strip() + except IOError: + val = 0 + try: + with open(input.replace('input', 'label')) as l: + name = l.read().strip() + except IOError: + name = '' + print ('{:9s}:{:3d}.{:03d}'.format( + name, (int(val) / 1000), (int(val) % 1000))), + count += 1 + if count % 3 == 0: + print + else: + print ('\t'), + print + + if dimmtemp_path: + os.chdir(dimmtemp_path) + print + print 'DIMM temperature' + count = 0 + for input in glob.glob('temp*_input'): + try: + with open(input) as f: + val = f.read().strip() + except IOError: + val = 0 + try: + with open(input.replace('input', 'label')) as l: + name = l.read().strip() + except IOError: + name = '' + print ('{:9s}:{:3d}.{:03d}'.format( + name, (int(val) / 1000), (int(val) % 1000))), + count += 1 + if count % 3 == 0: + print + else: + print ('\t'), + print + else: + os.chdir(hwmon_path) + for dirpath, dirnames, files in os.walk(hwmon_path): + for d in dirnames: + try: + with open('{}/{}'.format(d, 'name')) as f: + hwmon_name = f.read().strip() + if hwmon_name == dimmtemp_name_match: + dimmtemp_path = os.path.abspath(d) + dimmtemp_name = hwmon_name + except: + continue + + time.sleep(1) +except KeyboardInterrupt: + print " exiting..." diff --git a/meta-openbmc-mods/meta-common/recipes-utilities/peci-hwmon-test/peci-hwmon-test.bb b/meta-openbmc-mods/meta-common/recipes-utilities/peci-hwmon-test/peci-hwmon-test.bb new file mode 100644 index 000000000..b3b4096a6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-utilities/peci-hwmon-test/peci-hwmon-test.bb @@ -0,0 +1,20 @@ +SUMMARY = "PECI hwmon test tool" +DESCRIPTION = "command line python tool for testing PECI hwmon" + +SRC_URI = "\ + file://peci-hwmon-test.py \ + " +LICENSE = "CLOSED" + +RDEPENDS_${PN} += "python" + +S = "${WORKDIR}" + +do_compile () { +} + +do_install () { + install -d ${D}/${bindir} + install -m 0755 ${WORKDIR}/peci-hwmon-test.py ${D}/${bindir} +} + |