Skip to content

Commit

Permalink
bmc: add network bridge support
Browse files Browse the repository at this point in the history
This data handler listens on a TCP port for the image bytes.

Signed-off-by: Benjamin Fair <benjaminfair@google.com>
Change-Id: I388fdd7303c5f8bc93a8f75e97b3abf8adbb81af
  • Loading branch information
BenjaminFair committed Nov 21, 2019
1 parent 1290198 commit 545f565
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 10 deletions.
36 changes: 26 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ The required parameters are:
Parameter | Options | Meaning
----------- | -------- | -------
`command` | `update` | The tool should try to update the BMC firmware.
`interface` | `ipmibt`, `ipmilpc`, `ipmipci` | The data transport mechanism, typically `ipmilpc`
`interface` | `ipmibt`, `ipmilpc`, `ipmipci`, `ipminet` | The data transport mechanism, typically `ipmilpc`
`image` | path | The BMC firmware image file (or tarball)
`sig` | path | The path to a signature file to send to the BMC along with the image file.
`type` | blob ending | The ending of the blob id. For instance `/flash/image` becomes a type of `image`.
Expand All @@ -75,12 +75,17 @@ If you're using an LPC data transfer mechanism, you'll need two additional
parameters: `address` and `length`. These values indicate where on the host
you've reserved memory to be used for the transfer window.

If you're using a net data transfer mechanism, you'll also need two additional
parameters: `hostname` and `port`. These specify which address and port the tool
should attempt to connect to the BMC using.

## Introduction

This supports two methods of providing the image to stage. You can send the
This supports three methods of providing the image to stage. You can send the
file over IPMI packets, which is a very slow process. A 32-MiB image can take
~3 hours to send via this method. This can be done in <1 minutes via the PCI
bridge, or just a few minutes via LPC depending on the size of the mapped area.
~3 hours to send via this method. This can be done in <1 minutes via the PCI or
net bridge, or just a few minutes via LPC depending on the size of the mapped
area.

This is implemented as a phosphor blob handler.

Expand Down Expand Up @@ -130,13 +135,15 @@ Option | Meaning
The following are configuration options for how the host and BMC are meant to
transfer the data. By default, the data-in-IPMI mechanism is enabled.

There are two configurable data transport mechanisms, either staging the bytes
via the LPC memory region, or the PCI-to-AHB memory region. Because there is
only one `MAPPED_ADDRESS` variable at present, a platform should only configure
one. The platform's device-tree may have the region locked to a specific
driver (lpc-aspeed-ctrl), preventing the region from other use.
There are three configurable data transport mechanisms, either staging the bytes
via the LPC memory region, the PCI-to-AHB memory region, or sending over a
network connection. Because there is only one `MAPPED_ADDRESS` variable at
present, a platform should not configure LPC and P2A at the same time. The
platform's device-tree may have the region locked to a specific driver
(lpc-aspeed-ctrl), preventing the region from other use.

***NOTE:*** It will likely be possible to configure both in the near future.
***NOTE:*** It will likely be possible to configure both LPC and P2A in the near
future.

Variable | Default | Meaning
--------------------- | ------- | -------
Expand All @@ -159,6 +166,15 @@ Option | Meaning
`--enable-aspeed-lpc` | Use with ASPEED parts.
`--enable-nuvoton-lpc` | Use with Nuvoton parts.

A platform may also enable the network transport mechanism.

NOTE: This mechanism is only intended to be used in-band and not exposed
externally, as it doesn't implement any encryption or integrity verification.

Option | Meaning
----------------------| -------
`--enable-net-bridge` | Enable net transport bridge

There are also options to control an optional clean up mechanism.

Option | Meaning
Expand Down
4 changes: 4 additions & 0 deletions bmc/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ if ENABLE_NUVOTON_P2A_MBOX
libfirmwareblob_common_la_SOURCES += pci_nuvoton_handler.cpp
endif

if ENABLE_NET_BRIDGE
libfirmwareblob_common_la_SOURCES += net_handler.cpp
endif

libfirmwareblob_common_la_CXXFLAGS = \
-I$(top_srcdir) \
$(SDBUSPLUS_CFLAGS) \
Expand Down
8 changes: 8 additions & 0 deletions bmc/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "lpc_aspeed.hpp"
#include "lpc_handler.hpp"
#include "lpc_nuvoton.hpp"
#include "net_handler.hpp"
#include "pci_handler.hpp"
#include "status.hpp"
#include "util.hpp"
Expand Down Expand Up @@ -71,6 +72,10 @@ LpcDataHandler lpcDataHandler(
PciDataHandler pciDataHandler(MAPPED_ADDRESS, memoryRegionSize);
#endif

#ifdef ENABLE_NET_BRIDGE
NetDataHandler netDataHandler;
#endif

std::vector<DataHandlerPack> supportedTransports = {
{FirmwareFlags::UpdateFlags::ipmi, nullptr},
#ifdef ENABLE_PCI_BRIDGE
Expand All @@ -79,6 +84,9 @@ std::vector<DataHandlerPack> supportedTransports = {
#ifdef ENABLE_LPC_BRIDGE
{FirmwareFlags::UpdateFlags::lpc, &lpcDataHandler},
#endif
#ifdef ENABLE_NET_BRIDGE
{FirmwareFlags::UpdateFlags::net, &netDataHandler},
#endif
};

/**
Expand Down
153 changes: 153 additions & 0 deletions bmc/net_handler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright 2019 Google Inc.
*
* 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 "net_handler.hpp"

#include <errno.h>
#include <netinet/in.h>
#include <poll.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstdio>

namespace ipmi_flash
{

bool NetDataHandler::open()
{
listenFd.reset(::socket(AF_INET6, SOCK_STREAM, 0));
if (*listenFd < 0)
{
std::perror("Failed to create socket");
(void)listenFd.release();
return false;
}

struct sockaddr_in6 listenAddr;
listenAddr.sin6_family = AF_INET6;
listenAddr.sin6_port = htons(listenPort);
listenAddr.sin6_flowinfo = 0;
listenAddr.sin6_addr = in6addr_any;
listenAddr.sin6_scope_id = 0;

if (::bind(*listenFd, (struct sockaddr*)&listenAddr, sizeof(listenAddr)) <
0)
{
std::perror("Failed to bind");
return false;
}

if (::listen(*listenFd, 1) < 0)
{
std::perror("Failed to listen");
return false;
}
return true;
}

bool NetDataHandler::close()
{
connFd.reset();
listenFd.reset();

return true;
}

std::vector<std::uint8_t> NetDataHandler::copyFrom(std::uint32_t length)
{
if (!connFd)
{
struct pollfd fds;
fds.fd = *listenFd;
fds.events = POLLIN;

int ret = ::poll(&fds, 1, timeoutS * 1000);
if (ret < 0)
{
std::perror("Failed to poll");
return std::vector<uint8_t>();
}
else if (ret == 0)
{
fprintf(stderr, "Timed out waiting for connection\n");
return std::vector<uint8_t>();
}
else if (fds.revents != POLLIN)
{
fprintf(stderr, "Invalid poll state: 0x%x\n", fds.revents);
return std::vector<uint8_t>();
}

connFd.reset(::accept(*listenFd, nullptr, nullptr));
if (*connFd < 0)
{
std::perror("Failed to accept connection");
(void)connFd.release();
return std::vector<uint8_t>();
}

struct timeval tv = {};
tv.tv_sec = timeoutS;
if (setsockopt(*connFd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
{
std::perror("Failed to set receive timeout");
return std::vector<uint8_t>();
}
}

std::vector<std::uint8_t> data(length);

std::uint32_t bytesRead = 0;
ssize_t ret;
do
{
ret = read(*connFd, data.data() + bytesRead, length - bytesRead);
if (ret < 0)
{
if (errno == EINTR || errno == EAGAIN)
continue;
std::perror("Failed to read from socket");
break;
}

bytesRead += ret;
} while (ret > 0 && bytesRead < length);

if (bytesRead != length)
{
fprintf(stderr,
"Couldn't read full expected amount. Wanted %u but got %u\n",
length, bytesRead);
data.resize(bytesRead);
}

return data;
}

bool NetDataHandler::writeMeta(const std::vector<std::uint8_t>& configuration)
{
// TODO: have the host tool send the expected IP address that it will
// connect from
return true;
}

std::vector<std::uint8_t> NetDataHandler::readMeta()
{
return std::vector<std::uint8_t>();
}

} // namespace ipmi_flash
45 changes: 45 additions & 0 deletions bmc/net_handler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include "data_handler.hpp"

#include <unistd.h>

#include <cstdint>
#include <optional>
#include <stdplus/handle/managed.hpp>
#include <vector>

namespace ipmi_flash
{

/**
* Data Handler for receiving the image over a network port
*/
class NetDataHandler : public DataInterface
{
public:
NetDataHandler() : listenFd(std::nullopt), connFd(std::nullopt)
{
}

bool open() override;
bool close() override;
std::vector<std::uint8_t> copyFrom(std::uint32_t length) override;
bool writeMeta(const std::vector<std::uint8_t>& configuration) override;
std::vector<std::uint8_t> readMeta() override;

static constexpr std::uint16_t listenPort = 623;
static constexpr int timeoutS = 5;

private:
static void closefd(int&& fd)
{
::close(fd);
}
using Fd = stdplus::Managed<int>::Handle<closefd>;

Fd listenFd;
Fd connFd;
};

} // namespace ipmi_flash
8 changes: 8 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,14 @@ AS_IF([test "x$enable_nuvoton_p2a_vga" = "xyes"], [
])
])

AC_ARG_ENABLE([net-bridge],
AS_HELP_STRING([--enable-net-bridge],
[Enable external transfers using a TCP connection]))
AM_CONDITIONAL(ENABLE_NET_BRIDGE, [test "x$enable_net_bridge" = "xyes"])
AS_IF([test "x$enable_net_bridge" = "xyes"], [
AX_APPEND_COMPILE_FLAGS([-DENABLE_NET_BRIDGE], [CXXFLAGS])
])

AC_ARG_VAR(
STATIC_HANDLER_STAGED_NAME,
[The file to use for staging the firmware update.]
Expand Down
3 changes: 3 additions & 0 deletions flags.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class FirmwareFlags
ipmi = (1 << 8), /* Expect to send contents over IPMI BlockTransfer. */
p2a = (1 << 9), /* Expect to send contents over P2A bridge. */
lpc = (1 << 10), /* Expect to send contents over LPC bridge. */
/* New bridges starting with net densely pack the rest of the bits */
net = (1 << 11), /* Expect to send contents over network bridge. */
/* nextBridge = (2 << 11) */
};
};

Expand Down

0 comments on commit 545f565

Please sign in to comment.