Re: [PATCH v4] drivers/misc: Add Aspeed LPC control driver

From: Andy Shevchenko
Date: Tue Feb 07 2017 - 19:06:44 EST


On Wed, Feb 8, 2017 at 1:42 AM, Cyril Bur <cyrilbur@xxxxxxxxx> wrote:
> In order to manage server systems, there is typically another processor
> known as a BMC (Baseboard Management Controller) which is responsible
> for powering the server and other various elements, sometimes fans,
> often the system flash.
>
> The Aspeed BMC family which is what is used on OpenPOWER machines and a
> number of x86 as well is typically connected to the host via an LPC
> (Low Pin Count) bus (among others).

Perhaps I missed the discussion on previous versions, but here my
question. If it's available on x86, how you access it there? This
driver seems too OF oriented which is not a case on most of x86
platforms.

>
> The LPC bus is an ISA bus on steroids. It's generally used by the
> BMC chip to provide the host with access to the system flash (via MEM/FW
> cycles) that contains the BIOS or other host firmware along with a
> number of SuperIO-style IOs (via IO space) such as UARTs, IPMI
> controllers.
>
> On the BMC chip side, this is all configured via a bunch of registers
> whose content is related to a given policy of what devices are exposed
> at a per system level, which is system/vendor specific, so we don't want
> to bolt that into the BMC kernel. This started with a need to provide
> something nicer than /dev/mem for user space to configure these things.
>
> One important aspect of the configuration is how the MEM/FW space is
> exposed to the host (ie, the x86 or POWER). Some registers in that
> bridge can define a window remapping all or portion of the LPC MEM/FW
> space to a portion of the BMC internal bus, with no specific limits
> imposed in HW.
>
> I think it makes sense to ensure that this window is configured by a
> kernel driver that can apply some serious sanity checks on what it is
> configured to map.
>
> In practice, user space wants to control this by flipping the mapping
> between essentially two types of portions of the BMC address space:
>
> - The flash space. This is a region of the BMC MMIO space that
> more/less directly maps the system flash (at least for reads, writes
> are somewhat more complicated).
>
> - One (or more) reserved area(s) of the BMC physical memory.
>
> The latter is needed for a number of things, such as avoiding letting
> the host manipulate the innards of the BMC flash controller via some
> evil backdoor, we want to do flash updates by routing the window to a
> portion of memory (under control of a mailbox protocol via some
> separate set of registers) which the host can use to write new data in
> bulk and then request the BMC to flash it. There are other uses, such
> as allowing the host to boot from an in-memory flash image rather than
> the one in flash (very handy for continuous integration and test, the
> BMC can just download new images).
>
> It is important to note that due to the way the Aspeed chip lets the
> kernel configure the mapping between host LPC addresses and BMC ram
> addresses the offset within the window must be a multiple of size.
> Not doing so will fragment the accessible space rather than simply
> moving 'zero' upwards. This is caused by the nature of HICR8 being a
> mask and the way host LPC addresses are translated.

> drivers/misc/Kconfig | 8 ++
> drivers/misc/Makefile | 1 +
> drivers/misc/aspeed-lpc-ctrl.c | 263 +++++++++++++++++++++++++++++++++++

> include/uapi/linux/aspeed-lpc-ctrl.h | 36 +++++

Since it's UAPI can it be more generic in case there will be other LPC
implementations / devices?

> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -53,6 +53,7 @@ obj-$(CONFIG_ECHO) += echo/
> obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
> obj-$(CONFIG_CXL_BASE) += cxl/
> obj-$(CONFIG_PANEL) += panel.o

> +obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o

Does it suit best under misc?

> +static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
> + unsigned long param)
> +{

> + long rc;
> + struct aspeed_lpc_ctrl_mapping map;
> + struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file);
> + void __user *p = (void __user *)param;
> + u32 addr;
> + u32 size;

Reversed tree?

> + rc = regmap_write(lpc_ctrl->regmap, HICR7,
> + (addr | (map.addr >> 16)));
> + if (rc)
> + return rc;
> +
> + return regmap_write(lpc_ctrl->regmap, HICR8,
> + (~(map.size - 1)) | ((map.size >> 16) - 1));

Magic stuff above and here. Perhaps some helpers?

> + }
> +
> + return -EINVAL;
> +}
> +
> +static const struct file_operations aspeed_lpc_ctrl_fops = {

> + .owner = THIS_MODULE,

Do we still need this?

> + .mmap = aspeed_lpc_ctrl_mmap,
> + .unlocked_ioctl = aspeed_lpc_ctrl_ioctl,
> +};

> +static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
> +{
> + struct aspeed_lpc_ctrl *lpc_ctrl;
> + struct device *dev;
> + struct device_node *node;
> + struct resource resm;
> + int rc;
> +

> + dev = &pdev->dev;

You can do this in the definition block.


> + node = of_parse_phandle(dev->of_node, "flash", 0);
> + if (!node) {
> + dev_err(dev, "Didn't find host pnor flash node\n");
> + return -ENODEV;
> + }
> +

> + rc = of_property_read_u32_index(node, "reg", 3,
> + &lpc_ctrl->pnor_size);
> + if (rc)
> + return rc;
> + rc = of_property_read_u32_index(node, "reg", 2,
> + &lpc_ctrl->pnor_base);
> + if (rc)
> + return rc;

Isn't something you may read at once?

> + lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
> + lpc_ctrl->miscdev.name = DEVICE_NAME;
> + lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops;
> + lpc_ctrl->miscdev.parent = dev;

> + rc = misc_register(&lpc_ctrl->miscdev);

No devm_ variant?

> + if (rc)
> + dev_err(dev, "Unable to register device\n");
> + else
> + dev_info(dev, "Loaded at 0x%08x (0x%08x)\n",

%pa
%pa

> + lpc_ctrl->mem_base, lpc_ctrl->mem_size);
> +
> + return rc;
> +}

> +struct aspeed_lpc_ctrl_mapping {
> + __u8 window_type;
> + __u8 window_id;
> + __u16 flags;
> + __u32 addr;
> + __u32 offset;
> + __u32 size;
> +};

It will suck. So, no room for manoeuvre?

--
With Best Regards,
Andy Shevchenko