4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj/*
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * CDDL HEADER START
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj *
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * The contents of this file are subject to the terms of the
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * Common Development and Distribution License (the "License").
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * You may not use this file except in compliance with the License.
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj *
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * or http://www.opensolaris.org/os/licensing.
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * See the License for the specific language governing permissions
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * and limitations under the License.
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj *
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * When distributing Covered Code, include this CDDL HEADER in each
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * If applicable, add the following below this CDDL HEADER, with the
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * fields enclosed by brackets "[]" replaced with your own identifying
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * information: Portions Copyright [yyyy] [name of copyright owner]
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj *
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * CDDL HEADER END
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj */
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj/*
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * Use is subject to license terms.
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj */
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj#pragma ident "%Z%%M% %I% %E% SMI"
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj#include <libipmi.h>
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj#include <string.h>
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj#include "ipmi_impl.h"
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj/*
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * Extracts bits between index h (high, inclusive) and l (low, exclusive) from
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * u, which must be an unsigned integer.
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj */
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj#define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robjtypedef struct ipmi_fru_read
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj{
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj uint8_t ifr_devid;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj uint8_t ifr_offset_lsb;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj uint8_t ifr_offset_msb;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj uint8_t ifr_count;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj} ipmi_fru_read_t;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj/*
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * returns: size of FRU inventory data in bytes, on success
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * -1, otherwise
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj */
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robjint
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robjipmi_fru_read(ipmi_handle_t *ihp, ipmi_sdr_fru_locator_t *fru_loc, char **buf)
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj{
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj ipmi_cmd_t cmd, *resp;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj uint8_t count, devid;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj uint16_t sz, offset = 0;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj ipmi_fru_read_t cmd_data_in;
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj char *tmp;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj devid = fru_loc->_devid_or_slaveaddr._logical._is_fl_devid;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj /*
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * First we issue a command to retrieve the size of the specified FRU's
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * inventory area
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj */
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd.ic_netfn = IPMI_NETFN_STORAGE;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd.ic_cmd = IPMI_CMD_GET_FRU_INV_AREA;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd.ic_data = &devid;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd.ic_dlen = sizeof (uint8_t);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd.ic_lun = 0;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj if ((resp = ipmi_send(ihp, &cmd)) == NULL)
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj return (-1);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj if (resp->ic_dlen != 3) {
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj return (-1);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj }
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&sz, resp->ic_data, sizeof (uint16_t));
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj if ((tmp = malloc(sz)) == NULL) {
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj return (-1);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj }
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj while (offset < sz) {
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd_data_in.ifr_devid = devid;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd_data_in.ifr_offset_lsb = BITX(offset, 7, 0);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd_data_in.ifr_offset_msb = BITX(offset, 15, 8);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj if ((sz - offset) < 128)
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd_data_in.ifr_count = sz - offset;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj else
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd_data_in.ifr_count = 128;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd.ic_netfn = IPMI_NETFN_STORAGE;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd.ic_cmd = IPMI_CMD_READ_FRU_DATA;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd.ic_data = &cmd_data_in;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd.ic_dlen = sizeof (ipmi_fru_read_t);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj cmd.ic_lun = 0;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj if ((resp = ipmi_send(ihp, &cmd)) == NULL) {
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj free(tmp);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj return (-1);
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj }
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&count, resp->ic_data, sizeof (uint8_t));
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj if (count != cmd_data_in.ifr_count) {
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH,
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj NULL);
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj free(tmp);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj return (-1);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj }
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj (void) memcpy(tmp+offset, (char *)(resp->ic_data)+1, count);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj offset += count;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj }
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj *buf = tmp;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj return (sz);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj}
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robjint
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robjipmi_fru_parse_product(ipmi_handle_t *ihp, char *fru_area,
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj ipmi_fru_prod_info_t *buf)
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj{
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj ipmi_fru_hdr_t fru_hdr;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj char *tmp;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj uint8_t len, typelen;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj /*
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * We get the offset to the product info area from the FRU common
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * header which is at the start of the FRU inventory area.
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj *
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * The product info area is optional, so if the offset is NULL,
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * indicating that it doesn't exist, then we return an error.
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj */
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj if (!fru_hdr.ifh_product_info_off) {
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj return (-1);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj }
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj tmp = fru_area + (fru_hdr.ifh_product_info_off * 8) + 3;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj len = BITX(typelen, 5, 0);
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_manuf_name);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj tmp += len + 1;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj len = BITX(typelen, 5, 0);
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj ipmi_decode_string((typelen >> 6), len, tmp+1,
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj buf->ifpi_product_name);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj tmp += len + 1;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj len = BITX(typelen, 5, 0);
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_part_number);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj tmp += len + 1;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj len = BITX(typelen, 5, 0);
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj ipmi_decode_string((typelen >> 6), len, tmp+1,
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj buf->ifpi_product_version);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj tmp += len + 1;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj len = BITX(typelen, 5, 0);
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj ipmi_decode_string((typelen >> 6), len, tmp+1,
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj buf->ifpi_product_serial);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj tmp += len + 1;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj len = BITX(typelen, 5, 0);
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_asset_tag);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj return (0);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj}
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj/*
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * The Board Info area is described in Sect 11 of the IPMI Platform Management
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * FRU Information Storage Definition (v1.1).
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj */
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robjint
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robjipmi_fru_parse_board(ipmi_handle_t *ihp, char *fru_area,
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj ipmi_fru_brd_info_t *buf)
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj{
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj ipmi_fru_hdr_t fru_hdr;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj char *tmp;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj uint8_t len, typelen;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj /*
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * We get the offset to the board info area from the FRU common
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * header which is at the start of the FRU inventory area.
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj *
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * The board info area is optional, so if the offset is NULL,
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * indicating that it doesn't exist, then we return an error.
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj */
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj if (!fru_hdr.ifh_board_info_off) {
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj return (-1);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj }
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj tmp = fru_area + (fru_hdr.ifh_board_info_off * 8) + 3;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(buf->ifbi_manuf_date, tmp, 3);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj tmp += 3;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj len = BITX(typelen, 5, 0);
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_manuf_name);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj tmp += len + 1;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj len = BITX(typelen, 5, 0);
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_board_name);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj tmp += len + 1;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj len = BITX(typelen, 5, 0);
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj ipmi_decode_string((typelen >> 6), len, tmp+1,
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj buf->ifbi_product_serial);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj tmp += len + 1;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj (void) memcpy(&typelen, tmp, sizeof (uint8_t));
2cb5535af222653abf2eba5c180ded4a7b85d8b6robj len = BITX(typelen, 5, 0);
2eeaed14a5e2ed9bd811643ad5bffc3510ca0310robj ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_part_number);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj return (0);
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj}