Most UEFI protocols are reported as "unsupported" - uefi

I'm writing an EFI executable on a SoC device (Up Board) to help us automate BIOS updates and PXE boots for installating our software on numerous devices.
The problem I have is that seemingly most of the protocols in the specification are "unsupported" on this platform, even basic file system tasks. The only one I've successfully used is the LOADED_IMAGE_PROTOCOL. I'm using gnu-efi to compile the code, basing my code to this example https://mjg59.dreamwidth.org/18773.html. Is there something I'm doing wrong, or is this simply the firmware absolutely not implementing any of the protocols?
The American Megatrends utility "AfuEfix64.efi" is capable of retrieving BIOS information, and the SoC BIOS updates are done using an Intel's EFI executable. Both are closed source, unfortunately. My initial idea would have been to write a script that parses the output from these executables, but I don't think the EFI shell has much features for this type of tasks, so I moved on to writing an EFI executable to perform this.
Minimal code showing this:
#include <efi.h>
#include <efilib.h>
#include <x86_64/efibind.h>
// gnu-efi does not currently define firmware management
// https://raw.githubusercontent.com/tianocore/edk2/master/MdePkg/Include/Protocol/FirmwareManagement.h
#include "efifirmware.h"
// gnu-efi does not currently define this
#define EFI_LOAD_FILE2_PROTOCOL_GUID \
{ 0x4006c0c1, 0xfcb3, 0x403e, \
{ 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d }}
typedef EFI_LOAD_FILE_PROTOCOL EFI_LOAD_FILE2_PROTOCOL;
void tryProtocol(EFI_GUID proto_guid, void** out, const CHAR16* name,
EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) {
EFI_STATUS status;
status = uefi_call_wrapper(systemTable->BootServices->HandleProtocol, 3,
imageHandle, &proto_guid, out);
if (EFI_ERROR(status)) {
Print(L"HandleProtocol error for %s: %r\n", name, status);
} else {
Print(L"Protocol %s is supported\n", name);
}
}
void tryProtocols(EFI_HANDLE imgh, EFI_SYSTEM_TABLE* syst) {
EFI_LOADED_IMAGE* loaded_image = NULL;
EFI_GUID guid_imgprot = LOADED_IMAGE_PROTOCOL;
tryProtocol(guid_imgprot, (void**)&loaded_image,
L"LOADED_IMAGE_PROTOCOL", imgh, syst);
Print(L"Image base: %lx\n", loaded_image->ImageBase);
Print(L"Image size: %lx\n", loaded_image->ImageSize);
Print(L"Image file: %s\n", DevicePathToStr(loaded_image->FilePath));
EFI_FIRMWARE_MANAGEMENT_PROTOCOL* fw_manage = NULL;
EFI_GUID guid_fwman_prot = EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
tryProtocol(guid_fwman_prot, (void**)&fw_manage,
L"FIRMWARE_MANAGEMENT_PROTOCOL", imgh, syst);
EFI_LOAD_FILE_PROTOCOL* load_file = NULL;
EFI_GUID guid_loadf_prot = EFI_LOAD_FILE_PROTOCOL_GUID;
tryProtocol(guid_loadf_prot, (void**)&load_file,
L"LOAD_FILE_PROTOCOL", imgh, syst);
EFI_LOAD_FILE2_PROTOCOL* load_file2 = NULL;
EFI_GUID guid_loadf2_prot = EFI_LOAD_FILE2_PROTOCOL_GUID;
tryProtocol(guid_loadf2_prot, (void**)&load_file2,
L"LOAD_FILE2_PROTOCOL", imgh, syst);
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* simple_fs = NULL;
EFI_GUID guid_simple_fs_prot = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
tryProtocol(guid_simple_fs_prot, (void**)&simple_fs,
L"SIMPLE_FILE_SYSTEM_PROTOCOL", imgh, syst);
EFI_DISK_IO_PROTOCOL* disk = NULL;
EFI_GUID guid_disk_io_prot = EFI_DISK_IO_PROTOCOL_GUID;
tryProtocol(guid_disk_io_prot, (void**)&disk,
L"DISK_IO_PROTOCOL", imgh, syst);
EFI_BLOCK_IO_PROTOCOL* block = NULL;
EFI_GUID guid_block_io_prot = EFI_BLOCK_IO_PROTOCOL_GUID;
tryProtocol(guid_block_io_prot, (void**)&block,
L"BLOCK_IO_PROTOCOL", imgh, syst);
}
EFI_STATUS
EFIAPI
efi_main (EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) {
InitializeLib(imageHandle, systemTable);
Print(L"Image loaded\n");
tryProtocols(imageHandle, systemTable);
return EFI_SUCCESS;
}
Here's the output for running it:
EFI Shell version 2.40 [5.11]
Current running mode 1.1.2
Device mapping table
fs0 :HardDisk - Alias hd6b blk0
PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x0)/HD(1,GPT,2DCDDADD-8F3A-4A77-94A9-010A8C700BB8,0x800,0x100000)
fs1 :Removable HardDisk - Alias hd9g0a0b blk1
PciRoot(0x0)/Pci(0x14,0x0)/USB(0x6,0x0)/USB(0x0,0x0)/HD(1,MBR,0x528E6A1F,0x800,0x1CDE800)
blk0 :HardDisk - Alias hd6b fs0
PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x0)/HD(1,GPT,2DCDDADD-8F3A-4A77-94A9-010A8C700BB8,0x800,0x100000)
blk1 :Removable HardDisk - Alias hd9g0a0b fs1
PciRoot(0x0)/Pci(0x14,0x0)/USB(0x6,0x0)/USB(0x0,0x0)/HD(1,MBR,0x528E6A1F,0x800,0x1CDE800)
blk2 :HardDisk - Alias (null)
PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x0)/HD(2,GPT,8AC0F94E-3CA2-4C03-BE00-3A69721CC391,0x100800,0x1C1F7DF)
blk3 :BlockDevice - Alias (null)
PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x0)
blk4 :BlockDevice - Alias (null)
PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x1)
blk5 :BlockDevice - Alias (null)
PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x2)
blk6 :Removable BlockDevice - Alias (null)
PciRoot(0x0)/Pci(0x14,0x0)/USB(0x6,0x0)/USB(0x0,0x0)
Press ESC in 4 seconds to skip startup.nsh, any other key to continue.
fs1:\> tryprotocols.efi
Image loaded
Protocol LOADED_IMAGE_PROTOCOL is supported
Image base: 55BA6000
Image size: F000
Image file: \/tryprotocols.efi
HandleProtocol error for FIRMWARE_MANAGEMENT_PROTOCOL: Unsupported
HandleProtocol error for LOAD_FILE_PROTOCOL: Unsupported
HandleProtocol error for LOAD_FILE2_PROTOCOL: Unsupported
HandleProtocol error for SIMPLE_FILE_SYSTEM_PROTOCOL: Unsupported
HandleProtocol error for DISK_IO_PROTOCOL: Unsupported
HandleProtocol error for BLOCK_IO_PROTOCOL: Unsupported
fs1:\>
And here's the Makefile I use to compile it:
ARCH=x86_64
OBJS=tryprotocols.o
TARGET=tryprotocols.efi
TARGET_SO=$(TARGET:.efi=.so)
GNUEFIDIR=/home/gekko/gnu-efi-3.0.8
EFIINC=$(GNUEFIDIR)/inc
EFIINCS=-I$(EFIINC) -I$(EFIINC)/$(ARCH) -I$(EFIINC)/protocol
LIB=$(GNUEFIDIR)/$(ARCH)/lib
EFILIB=$(GNUEFIDIR)/gnuefi
EFI_CRT_OBJS=$(GNUEFIDIR)/$(ARCH)/gnuefi/crt0-efi-$(ARCH).o
EFI_LDS=$(EFILIB)/elf_$(ARCH)_efi.lds
CFLAGS=$(EFIINCS) -fno-stack-protector -fpic \
-fshort-wchar -mno-red-zone -Wall
ifeq ($(ARCH),x86_64)
CFLAGS += -DEFI_FUNCTION_WRAPPER
endif
LDFLAGS=-nostdlib -znocombreloc -T $(EFI_LDS) -shared \
-Bsymbolic -L $(EFILIB) -L $(LIB) $(EFI_CRT_OBJS)
all: $(TARGET)
$(TARGET_SO): $(OBJS)
ld $(LDFLAGS) $(OBJS) -o $# -lefi -lgnuefi
%.efi: %.so
objcopy -j .text -j .sdata -j .data -j .dynamic \
-j .dynsym -j .rel -j .rela -j .reloc \
--target=efi-app-$(ARCH) $^ $#
clean:
rm -f *.o
rm -f $(TARGET)
rm -f $(TARGET_SO)
EDIT: Modified version that properly uses LocateProtocol() to find the protocols, and uses them to open and close a file.
#include <efi.h>
#include <efilib.h>
#include <x86_64/efibind.h>
BOOLEAN tryProtocol(EFI_GUID proto_guid, void** out, const CHAR16* name,
EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) {
*out = NULL;
EFI_STATUS status;
EFI_HANDLE interface = NULL;
status = uefi_call_wrapper(systemTable->BootServices->LocateProtocol, 3,
&proto_guid, NULL, &interface);
if (EFI_ERROR(status)) {
Print(L"LocateProtocol error for %s: %r\n", name, status);
return FALSE;
}
Print(L"Locate protocol address: %s, %x\n", name, interface);
*out = interface;
return TRUE;
}
EFI_STATUS
EFIAPI
efi_main (EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) {
InitializeLib(imageHandle, systemTable);
Print(L"Image loaded\n");
EFI_LOADED_IMAGE* loaded_image = NULL;
EFI_GUID guid_imgprot = LOADED_IMAGE_PROTOCOL;
if (tryProtocol(guid_imgprot, (void**)&loaded_image,
L"LOADED_IMAGE_PROTOCOL", imageHandle, systemTable) != TRUE) {
Print(L"Missing required protocol. Aborting\n");
return EFI_SUCCESS;
}
Print(L"Image base: %lx\n", loaded_image->ImageBase);
Print(L"Image size: %lx\n", loaded_image->ImageSize);
Print(L"Image file: %s\n", DevicePathToStr(loaded_image->FilePath));
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* simple_fs = NULL;
EFI_GUID guid_simple_fs_prot = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
if (tryProtocol(guid_simple_fs_prot, (void**)&simple_fs,
L"EFI_SIMPLE_FILE_SYSTEM_PROTOCOL", imageHandle, systemTable) != TRUE) {
Print(L"Missing required protocol. Aborting\n");
return EFI_SUCCESS;
}
EFI_FILE_PROTOCOL* vol_proto = NULL;
EFI_STATUS status;
Print(L"dereffing\n");
Print(L"address of OpenVolume: %x\n", simple_fs->OpenVolume);
status = uefi_call_wrapper(simple_fs->OpenVolume, 2, simple_fs, &vol_proto);
if (EFI_ERROR(status)) {
Print(L"Error opening volume: %r\n", status);
return EFI_SUCCESS;
}
Print(L"SIMPLE_FILE_SYSTEM volume opened\n");
EFI_FILE_PROTOCOL* f;
CHAR16 fname[10] = L"foo.txt\0";
UINT64 openmode = EFI_FILE_MODE_READ;
UINT64 attr = 0;
status = uefi_call_wrapper(vol_proto->Open, 5, vol_proto, &f, fname, openmode, attr);
if (EFI_ERROR(status)) {
Print(L"Error opening file: %r\n", status);
return EFI_SUCCESS;
}
Print(L"opened file %s\n", fname);
// Spec says can only return EFI_SUCCESS
status = uefi_call_wrapper(vol_proto->Close, 1, f);
Print(L"Closed file\n");
return EFI_SUCCESS;
}

Where and how to access a specific protocol is not statically defined by the UEFI specification, but something that needs to be discovered at runtime. While a bit arduous, it makes it possible to write applications/drivers portable across vastly different UEFI implementations that all conform to the specification.
So you need to LocateProtocol() on each protocol you are intending to use before you can make use of HandleProtocol().
You get away with LOADED_IMAGE_PROTOCOL because that one is initialized with your ImageHandle referring to the instance of the currently executing image (your program).
This is covered in Matthew's post under the section That covers how to use protocols attached to the image handle. How about protocols that are attached to other handles?.

Related

Could not retrieve UEFI variable with EDK2 GetVariable2() while variable exists

I've created a nonvolatile UEFI variable via Linux efivarfs interface. I can get the value of the variable with efivar command line. But when I try to get the value of that variable during boot phase via EDK2 GetVariable2() I got "Not Found" error.
Here is the .dec file and C++ snippet:
[Guids]
gEfiMyTestGuid = {0x11c564cd, 0xb9f2, 0x4eb8, {0x94, 0x62, 0xe0, 0xba, 0x74, 0x56, 0x42, 0x36}}
extern EFI_GUID gEfiMyTestGuid;
....
....
VOID *Data;
UINTN DataSize;
EFI_STATUS Status;
Status = GetVariable2 (L"test", &gEfiMyTestGuid, &Data,
&DataSize);
if (EFI_ERROR (Status)) {
Print (L"Failure error: %r\n", Status);
return Status;
}

How to add recipe in yocto to build curl example?

I can build the test.c without using yocto in my terminal easily. So this means
there is no problem with the test.c or the libcurl in my host machine.
However, when I build using yocto it complains that it cannot find curl/curl.h.
DEBUG: Executing shell function do_compile
| test.c:5:10: fatal error: curl/curl.h: No such file or directory
| #include <curl/curl.h>
| ^~~~~~~~~~~~~
| compilation terminated.
| WARNING: exit code 1 from a shell command.
Here is my directory structure which contains my example based out of libcurl.
├── curlTest
│   ├── curlTest
│   │   ├── README.txt
│   │   └── test.c
│   └── curlTest.bb
Here is my curlTest.bb
curlTest.bb
SUMMARY = "Simple Hello World Application"
DESCRIPTION = "A test application to demonstrate how to create a recipe \
by directly compiling C files with BitBake."
SECTION = "examples"
PRIORITY = "optional"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "\
file://README.txt;md5=321180101a13e8916e6753497a4f9c82"
SRC_URI = "file://test.c \
file://README.txt"
S = "${WORKDIR}"
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} -o testCurl test.c -lcurl
}
do_install() {
install -d ${D}${bindir}
install -m 0755 hello ${D}${bindir}
}
Here is the example of libcurl based out of the libcurl website.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
struct MemoryStruct {
char *memory;
size_t size;
};
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
if(ptr == NULL) {
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
int main(void)
{
CURL *curl_handle;
CURLcode res;
struct MemoryStruct chunk;
chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
chunk.size = 0; /* no data at this point */
curl_global_init(CURL_GLOBAL_ALL);
/* init the curl session */
curl_handle = curl_easy_init();
/* specify URL to get */
curl_easy_setopt(curl_handle, CURLOPT_URL, "https://www.example.com/");
/* send all data to this function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
/* we pass our 'chunk' struct to the callback function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
/* some servers don't like requests that are made without a user-agent
field, so we provide one */
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
/* get it! */
res = curl_easy_perform(curl_handle);
/* check for errors */
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
else {
/*
* Now, our chunk.memory points to a memory block that is chunk.size
* bytes big and contains the remote file.
*
* Do something nice with it!
*/
printf("%lu bytes retrieved\n", (unsigned long)chunk.size);
}
/* cleanup curl stuff */
curl_easy_cleanup(curl_handle);
free(chunk.memory);
/* we're done with libcurl, so clean it up */
curl_global_cleanup();
return 0;
}

gss_acquire_cred on Windows

I'm trying to acquire credentials for Administrator on Windows host.
I'm under mingw64_shell.
Here is my credential:
$ klist
Credentials cache: FILE:/tmp/krb5cc_1049076
Principal: Administrator#CORP.PEROKSID.COM
Issued Expires Principal
Jan 4 10:14:07 2016 Jan 4 20:14:07 2016 krbtgt/CORP.PEROKSID.COM#CORP.PEROKSID.COM
Here is my code:
#include <stdio.h>
#include <string.h>
#include <gss.h>
static void doDisplay(const char *m,OM_uint32 code,int type)
{
OM_uint32 maj_stat, min_stat;
gss_buffer_desc msg;
OM_uint32 msg_ctx;
msg_ctx = 0;
while (1)
{
maj_stat = gss_display_status(&min_stat, code,
type, GSS_C_NULL_OID,
&msg_ctx, &msg);
printf("GSS-API error %s - type: %s code: %d, msg: %s\n", m,
type == GSS_C_GSS_CODE ? "major" : "minor",
code,
(char *)msg.value);
gss_release_buffer(&min_stat, &msg);
if (!msg_ctx)
break;
}
}
void displayError(const char *msg, OM_uint32 maj_stat, OM_uint32 min_stat)
{
doDisplay(msg, maj_stat, GSS_C_GSS_CODE);
doDisplay(msg, min_stat, GSS_C_MECH_CODE);
}
int getCreds(const char *service_name, gss_cred_id_t *server_creds)
{
printf ("Test name: %s\n", service_name);
gss_buffer_desc name_buf;
gss_name_t server_name;
OM_uint32 maj_stat, min_stat;
name_buf.value = service_name;
//name_buf.length = strlen(name_buf.value) + 1;
name_buf.length = strlen(name_buf.value);
maj_stat = gss_import_name(&min_stat, &name_buf,
(gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &server_name);
if (maj_stat != GSS_S_COMPLETE)
{
displayError("importing name", maj_stat, min_stat);
return -1;
}
maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
server_creds, NULL, NULL);
if (maj_stat != GSS_S_COMPLETE)
{
displayError("acquiring credentials", maj_stat, min_stat);
return -1;
}
(void) gss_release_name(&min_stat, &server_name);
return 0;
}
int main(int argc, char** argv) {
gss_cred_id_t gsscreds;
if(getCreds(argv[1], &gsscreds) != 0)
return 1;
}
I have compiled it as a.exe. I run it:
$ ./a.exe 'Administrator#CORP.PEROKSID.COM'
Test name: Administrator#CORP.PEROKSID.COM
GSS-API error acquiring credentials - type: major code: 458752, msg: No credentials were supplied, or the credentials were unavailable or inaccessible
GSS-API error acquiring credentials - type: minor code: 11, msg: No principal in keytab matches desired name
How I can fix this error?
You probably misunderstood the API. The servername you are importing with GSS_C_NT_HOSTBASED_SERVICE is your target server. gss_acquire_cred needs an initiate crdential. accept is for servers/services. Services do work with keytabs only and client with caches or client keytabs. Since you have a valid credential cache for a user principal, you want to initiate a context with a client redential.

Pull GPIO pin low as last act prior to shutting down

I want to signal a power off state by pulling a GPIO pin low for 10 seconds. I have discovered the run levels in the /etc/rc.* directories. Eventually the "halt" command gets run.
I'd like to edit and recompile the halt.c file, but I am unable to locate it. I have two questions:
Is this the right approach?
Where is the source code? (pending positive answer from Q1).
Thank you.
Justin
I haven't used the Raspberry Pi, but on most systems halt, poweroff and reboot are all links to the same binary. Another way to do this would be to just write a program to do what you want and make it the last thing to be called in /etc/rc.d/rc0.d. Runlevel 0 is what the system does when it shuts down.
Here is my take on this problem 'powerctl_pin'
https://pastebin.com/BdzGM5TJ
/*
Raspi3B Power Control Pin Raspbian Jessie
Bilgus -- 2017 CC-BY-SA 3.0
Requires WiringPI http://wiringpi.com/
Compiling:
gcc -o powerctl_pin powerctl_pin.c -l wiringPi && chmod +x powerctl_pin &&
sudo cp powerctl_pin /bin/
Install:
sudo /bin/powerctl_pin 21 install
Uninstall:
sudo /bin/powerctl_pin 21 uninstall
Shutdown:
sudo /bin/powerctl_pin 21 poweroff
/bin/powerctl_pin Broadcom_pin#, mode
Valid Modes: poweroff, poweron, install, uninstall
Works with mosfet latch switch
https://easyeda.com/Bilgus/New_Project-4ce1316bb1f6402985f8d1c4f196448d
Original Circuit:
http://www.mosaic-industries.com/embedded-systems/microcontroller-projects
/raspberry-pi/on-off-power-controller
*/
/*
Raspi3B Power Control Pin Raspbian Jessie
Bilgus -- 2017 CC-BY-SA 3.0
Requires WiringPI http://wiringpi.com/
Compiling:
gcc -o powerctl_pin powerctl_pin.c -l wiringPi && chmod +x powerctl_pin &&
sudo cp powerctl_pin /bin/
Install:
sudo /bin/powerctl_pin 21 install
Uninstall:
sudo /bin/powerctl_pin 21 uninstall
Shutdown:
sudo /bin/powerctl_pin 21 poweroff
/bin/powerctl_pin Broadcom_pin#, mode
Valid Modes: poweroff, poweron, install, uninstall
Works with mosfet latch switch
https://easyeda.com/Bilgus/New_Project-4ce1316bb1f6402985f8d1c4f196448d
Original Circuit:
http://www.mosaic-industries.com/embedded-systems/microcontroller-projects
/raspberry-pi/on-off-power-controller
*/
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <signal.h> //sigterm, sigint()
#include "wiringPi.h"
#define WIRINGPI_CODES 999 /* enable error checking on setup */
#define POWEROFF_SVC "PwrCtl_Pin.service"
#define POWEROFF_SVC_PATH "/lib/systemd/system/"POWEROFF_SVC
#define REQUIRES "shutdown.target umount.target final.target poweroff.target"
#define AFTER "shutdown.target umount.target final.target"
#define BEFORE "systemd-poweroff.service"
#define WANTEDBY "poweroff.target"
#define POWERINT_SVC "PwrCtl_Pin-Interrupt.service"
#define POWERINT_SVC_PATH "/lib/systemd/system/"POWERINT_SVC
static void poweroff_ISR_INIT(int pin);
static int g_pin = 0;
/* enables pullup - unneeded to hold latch */
static void poweron_pin(int pin)
{
pullUpDnControl(pin, PUD_UP);
pinMode(pin, INPUT);
}
/* pulls pin low in short loop to turn power off */
static void poweroff_pin(int pin)
{
pullUpDnControl(pin, PUD_OFF);
int c;
for(c=0;c<20;c++)
{
//pinMode(pin, INPUT);
//pullUpDnControl(pin, PUD_DOWN);
digitalWrite(pin,LOW);
pinMode(pin, OUTPUT);
digitalWrite(pin,LOW);
delay(500);
}
}
/* ISR Called when pin goes from high to low debounce checks that still low */
static void poweroff_ISR(void)
{
delay(100);
if (digitalRead(g_pin) == LOW) /* pin debounce */
{
openlog ("Poweroff ISR", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
syslog (LOG_NOTICE, "ISR Triggered - Shutdown -h now");
closelog();
system("shutdown -h now");
exit(0);
}
else
poweroff_ISR_INIT(g_pin);
}
/* signal handler recieves shutdown signals */
static void signal_handler(int signal_0)
{
if (signal_0 == SIGINT || signal_0 == SIGTERM || signal_0 == SIGSTOP)
exit(0);
}
/* Initialize Poweroff ISR handler */
static void poweroff_ISR_INIT(int pin)
{
g_pin = pin;
pinMode(pin, INPUT);
pullUpDnControl(pin, PUD_UP);
wiringPiISR(pin, INT_EDGE_FALLING, &poweroff_ISR);
while(1)
{
delay(100);
;;
}
}
/* creates service daemons */
static void install(int pin, char *prog_path)
{
FILE *f = fopen(POWEROFF_SVC_PATH, "w");
if (f == NULL)
{
fprintf(stderr, "Error opening file! %s\n", POWEROFF_SVC_PATH);
exit(-6);
}
fprintf(f, "# POWER CONTROL PIN SERVICE \n# %s\n\n", prog_path);
fprintf(f, "[Unit]\nDescription=PowerCtl_Pin pulls GPIO pin %i LOW\n", pin);
fprintf(f, "DefaultDependencies=no\n");
fprintf(f, "Requires=%s\n", REQUIRES);
fprintf(f, "After=%s\nBefore=%s\n\n", AFTER, BEFORE);
fprintf(f, "[Service]\nType=oneshot\n");
fprintf(f, "ExecStart=%s %i poweroff\n\n", prog_path, pin);
fprintf(f, "[Install]\nWantedBy=%s\n\n", WANTEDBY);
fclose(f);
printf("Service file created: %s\n", POWEROFF_SVC_PATH);
system("systemctl enable " POWEROFF_SVC);
FILE *f1 = fopen(POWERINT_SVC_PATH, "w");
if (f1 == NULL)
{
fprintf(stderr, "Error opening file! %s\n", POWERINT_SVC_PATH);
exit(-7);
}
fprintf(f1, "# POWER CONTROL PIN INTERRUPT SERVICE \n");
fprintf(f1, "# %s\n\n", prog_path);
fprintf(f1, "[Unit]\nDescription=PowerCtl_Pin Interrupt watches GPIO ");
fprintf(f1, "for pin %i LOW calls shutdown now\n", pin);
fprintf(f1, "DefaultDependencies=no\n");
fprintf(f1, "After=network.target\n\n");
fprintf(f1, "[Service]\nType=simple\n");
fprintf(f1, "ExecStart=%s %i interrupt &\n", prog_path, pin);
fprintf(f1, "TimeoutSec=0\nRestart=always\n\n");
fprintf(f1, "[Install]\nWantedBy=multi-user.target\n\n");
fclose(f1);
printf("\n\rService file created: %s\n", POWERINT_SVC_PATH);
system("systemctl enable " POWERINT_SVC);
printf("Attempting to start: %s\n\r", POWERINT_SVC_PATH);
system("systemctl start " POWERINT_SVC);
}/*install*/
/* disables/stops service daemons */
static void uninstall(int pin, char *prog_path)
{
system("systemctl disable " POWEROFF_SVC);
printf("Service file still exists: %s\n\n", POWEROFF_SVC_PATH);
system("systemctl stop " POWERINT_SVC);
system("systemctl disable " POWERINT_SVC);
printf("Service file still exists: %s\n", POWERINT_SVC_PATH);
}
int main(int argc, char **argv)
{
int pin = 0;
if (geteuid() != 0)
{
fprintf (stderr, "You need to be root to run this program. (sudo?)\n") ;
exit(-1);
}
putenv("WIRINGPI_CODES=WhOkNoWs??");
if (wiringPiSetupGpio() != 0) /* change to wiringPiSetup() for WiringPi Pins*/
{
fprintf(stderr, "wiringPiSetup error\n");
exit(-2);
}
if(argc < 3)
{
fprintf (stderr, "not enough args poweroff_pin(brcm_pin# mode)\n") ;
fprintf (stderr, "Valid Mode [power[off/on], interrupt, install, uninstall]])\n") ;
exit(-3);
}
else if (sscanf (argv[1],"%i", &pin) != 1)
{
fprintf (stderr, "invalid pin number, poweroff_pin(brcm_pin# mode) ");
fprintf (stderr, "Valid Mode [power[off/on], interrupt, install, uninstall]])\n") ;
exit(-4);
}
else if(strncmp(argv[2],"interrupt",10) == 0)
{
openlog (argv[0], LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
syslog (LOG_NOTICE, "%s - interrupt mode pin = %s", argv[0], argv[1]);
closelog();
if (signal(SIGINT, signal_handler) == SIG_ERR)
{
fprintf(stderr, "Unable to register signal handler\n");
exit(-5);
}
poweroff_ISR_INIT(pin);
}
else if(strncmp(argv[2],"poweroff",10) == 0)
{
poweroff_pin(pin);
}
else if(strncmp(argv[2],"install",10) == 0)
{
openlog (argv[0], LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
syslog (LOG_NOTICE, "%s - install pin = %s", argv[0], argv[1]);
closelog();
install(pin, argv[0]);
}
else if(strncmp(argv[2],"uninstall",10) == 0)
{
openlog (argv[0], LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
syslog (LOG_NOTICE, "%s - uninstall pin = %s", argv[0], argv[1]);
closelog();
uninstall(pin, argv[0]);
}
else
{
poweron_pin(pin);
}
return 0;
}/*main*/

llseek not behaving in kernel driver

I have been writing a lcd kernel driver for a LCD module. All was going well, I can write to the display, create a /dev/lcd node that I can write into and it will display the results on the screen. I thought using the llseek fops callback to position the cursor on the lcd would be good, this way I could use rewind fseek etc. However it is not working as I expected, below is a summary of what I am seeing:
The relevant lines of code from the driver side are:
loff_t lcd_llseek(struct file *filp, loff_t off, int whence)
{
switch (whence) {
case 0: // SEEK_SET
if (off > 4*LINE_LENGTH || off < 0) {
printk(KERN_ERR "unsupported SEEK_SET offset %llx\n", off);
return -EINVAL;
}
lcd_gotoxy(&lcd, off, 0, WHENCE_ABS);
break;
case 1: // SEEK_CUR
if (off > 4*LINE_LENGTH || off < -4*LINE_LENGTH) {
printk(KERN_ERR "unsupported SEEK_CUR offset %llx\n", off);
return -EINVAL;
}
lcd_gotoxy(&lcd, off, 0, WHENCE_REL);
break;
case 2: // SEEK_END (not supported, hence fall though)
default:
// how did we get here !
printk(KERN_ERR "unsupported seek operation\n");
return -EINVAL;
}
filp->f_pos = lcd.pos;
printk(KERN_INFO "lcd_llseek complete\n");
return lcd.pos;
}
int lcd_open(struct inode *inode, struct file *filp)
{
if (!atomic_dec_and_test(&lcd_available)) {
atomic_inc(&lcd_available);
return -EBUSY; // already open
}
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.write = lcd_write,
.llseek = lcd_llseek,
.open = lcd_open,
.release = lcd_release,
};
int lcd_init(void)
{
...
// allocate a new dev number (this can be dynamic or
// static if passed in as a module param)
if (major) {
devno = MKDEV(major, 0);
ret = register_chrdev_region(devno, 1, MODULE_NAME);
} else {
ret = alloc_chrdev_region(&devno, 0, 1, MODULE_NAME);
major = MAJOR(devno);
}
if (ret < 0) {
printk(KERN_ERR "alloc_chrdev_region failed\n");
goto fail;
}
// create a dummy class for the lcd
cl = class_create(THIS_MODULE, "lcd");
if (IS_ERR(cl)) {
printk(KERN_ERR "class_simple_create for class lcd failed\n");
goto fail1;
}
// create cdev interface
cdev_init(&cdev, &fops);
cdev.owner = THIS_MODULE;
ret = cdev_add(&cdev, devno, 1);
if (ret) {
printk(KERN_ERR "cdev_add failed\n");
goto fail2;
}
// create /sys/lcd/fplcd/dev so udev will add our device to /dev/fplcd
device = device_create(cl, NULL, devno, NULL, "lcd");
if (IS_ERR(device)) {
printk(KERN_ERR "device_create for fplcd failed\n");
goto fail3;
}
...
}
To test the lseek call I have the following unit test:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define log(msg, ...) fprintf(stdout, __FILE__ ":%s():[%d]:" msg, __func__, __LINE__, __VA_ARGS__)
int lcd;
void test(void)
{
int k;
// a lot of hello's
log("hello world test\n",1);
if (lseek(lcd, 0, SEEK_CUR) == -1) {
log("failed to seek\n", 1);
}
}
int main(int argc, char **argv)
{
lcd = open("/dev/lcd", O_WRONLY);
if (lcd == -1) {
perror("unable to open lcd");
exit(EXIT_FAILURE);
}
test();
close(lcd);
return 0;
}
The files are cross compiled like so:
~/Workspace/ts4x00/lcd-module$ cat Makefile
obj-m += fls_lcd.o
all:
make -C $(KPATH) M=$(PWD) modules
$(CROSS_COMPILE)gcc -g -fPIC $(CFLAGS) lcd_unit_test.c -o lcd_unit_test
clean:
make -C $(KPATH) M=$(PWD) clean
rm -rf lcd_unit_test
~/Workspace/ts4x00/lcd-module$ make CFLAGS+="-march=armv4 -ffunction-sections -fdata-sections"
make -C ~/Workspace/ts4x00/linux-2.6.29 M=~/Workspace/ts4x00/lcd-module modules
make[1]: Entering directory `~/Workspace/ts4x00/linux-2.6.29'
CC [M] ~/Workspace/ts4x00/lcd-module/fls_lcd.o
~/Workspace/ts4x00/lcd-module/fls_lcd.c:443: warning: 'lcd_entry_mode' defined but not used
Building modules, stage 2.
MODPOST 1 modules
CC ~/Workspace/ts4x00/lcd-module/fls_lcd.mod.o
LD [M] ~/Workspace/ts4x00/lcd-module/fls_lcd.ko
make[1]: Leaving directory `~/Workspace/ts4x00/linux-2.6.29'
~/Workspace/ts4x00/arm-2008q3/bin/arm-none-linux-gnueabi-gcc -g -fPIC -march=armv4 -ffunction-sections -fdata-sections lcd_unit_test.c -o lcd_unit_test
This is the output of running the driver with the unit test is:
root#ts4700:~/devel# insmod ./fls_lcd.ko
root#ts4700:~/devel# ./lcd_unit_test
lcd_unit_test.c:test():[61]:hello world test
lcd_unit_test.c:test():[63]:failed to seek
root#ts4700:~/devel# dmesg
FLS LCD driver started
unsupported SEEK_SET offset bf0a573c
I cannot figure out why the parameters are being mucked up so badly on the kernel side, I tried to SEEK_CUR to position 0 and in the driver I get a SEEK_SET (no matter what I put in the unit test) and a crazy big number for off?
Does anyone know what is going on please ?
btw I am compiling for kernel 2.6.29 on a arm dev kit
OK sorry guys after trying to debug this all last night it comes down to compiling against the wrong kernel (I had KPATH left to a different config of the kernel than was on the sdcard)
sorry for wasting everyones time, but hopefully if someone is seeing what looks like a crazy stack in their kernel driver this might set them straight.
oh and thanks for all the help :)