On Thu, Apr 7, 2022 at 2:17 PM Limonciello, Mario <Mario.Limonciello@xxxxxxx> wrote: > > [Public] > > > -----Original Message----- > > From: Jorge Lopez <jorgealtxwork@xxxxxxxxx> > > Sent: Thursday, April 7, 2022 08:45 > > To: Limonciello, Mario <Mario.Limonciello@xxxxxxx> > > Cc: Hans de Goede <hdegoede@xxxxxxxxxx>; platform-driver- > > x86@xxxxxxxxxxxxxxx > > Subject: Re: [PATCH v1 5/6] Sure Admin Security Feature > > > > Hans, Mario, > > > > The code links make references to code that implements the new > > interfaces but there’s > > still code in the kernel that uses the old ones. I do agree we should > > be forward looking > > and want to be good participants in the kernel development, but can’t > > let our immediate > > business needs be impacted with opportunities to enhance the driver to > > take advantage > > of the latest kernel features. > > > > Keep in mind that from an upstream kernel perspective this driver is "new". > There is no requirements from the upstream perspective to > keep your old interfaces in place from when it was out of tree. > > Requirements like this start when the driver is in tree. > https://www.kernel.org/doc/html/latest/admin-guide/abi-stable.html > Thank you for the link and explanation regarding the kernel perspective for the state of the driver. > > Rewriting those security features will impact customer business > > datelines requiring > > HP to provide private releases as the kernel version changes. The > > requested changes > > will impact products in the market and HP ability to help customers to > > migrate to Linux > > from Windows products. > > > > It is because of the immediate business needs, avoiding impacting our > > customers/products, > > and rewriting enhancements to the driver that I need to propose an > > interim solution. > > Right, get it upstream and it's less work, of course 😊 Indeed that is our goal. > > > > > My proposal is to introduce a read/write value accessible in the user > > space to control how > > the driver reports and handles BIOS settings and values. The new > > configuration features > > will be gradually disabled as they are converted to use the standardized API. > > It is like the configuration flag used to overcome the tablet detection > > problem > > introduced in the past. The changes solely affect the HP WMI driver. > > This option will help us > > move forward for this release and give us time to make the necessary > > changes to both > > the driver and support applications. > > > > Please let me know if this is a viable interim solution. > > > > If it is not possible, I need to ask where the actual written > > requirement is found so I can > > include them in the business justification for changes and release > > delays to management. > > > > From an upstream subsystem maintainer perspective Hans will want you to do > everything you can to get it correct the first time. If you don't; what impetus do > you have to fix it later? You would have an interface that "works" and meets > your business needs but no guarantee it ever would standardize. > > I do get where you're coming from though and you obviously have customers that > value your existing interface. So let me entertain this thought process a little bit, > Hans feel free to disagree with me. > > If there is a compatibility mode to let it work similar to how it worked "out > of tree" I don't think this should be something that you turn on/off dynamically > at runtime. It is a lot of code, you introduce complexity of sysfs files coming and > going and racing with userspace and worse this eventually this would all be dead code. > I think it's better to have a KConfig option that would enable this dead code. > > Also I think a better place for such a file that we would all know is going to eventually > go away is debugfs. There is no guarantee for files and interfaces in debugfs to stay > around. You can point applications at using the sysfs file today with your out of tree > driver there until the standardized interface support is made available. > Something like CONFIG_HP_WMI_DEBUGFS_SETTINGS. Then you can keep it in there > as long as you want, distros and users can decide if they want to use it. When you're > confident enough that all the applications that previously used the out of tree module > are migrated to the modern interface tear away that debugfs option as a future patch. > I agree with both your comments and Hans request. . Hans is correct in requesting the driver to use the latest standards and be correct the first time, I am working with the security architect on a timeline to made the requested changes and submit a new review. Is there are a site where the kernel schedules are published? This information will help us align better in the future and setup the correct speciation. > > > > Regards, > > > > Jorge Lopez > > > > > > On Tue, Apr 5, 2022 at 12:13 PM Limonciello, Mario > > <Mario.Limonciello@xxxxxxx> wrote: > > > > > > [Public] > > > > > > > > > > > > > -----Original Message----- > > > > From: Jorge Lopez <jorgealtxwork@xxxxxxxxx> > > > > Sent: Tuesday, April 5, 2022 11:52 > > > > To: Hans de Goede <hdegoede@xxxxxxxxxx> > > > > Cc: Limonciello, Mario <Mario.Limonciello@xxxxxxx>; platform-driver- > > > > x86@xxxxxxxxxxxxxxx > > > > Subject: Re: [PATCH v1 5/6] Sure Admin Security Feature > > > > > > > > Hi Hans, > > > > > > > > On Tue, Apr 5, 2022 at 6:54 AM Hans de Goede > > <hdegoede@xxxxxxxxxx> > > > > wrote: > > > > > > > > > > Hi, > > > > > > > > > > On 4/4/22 23:59, Limonciello, Mario wrote: > > > > > > [Public] > > > > > > > > > > > > > > > > > > > > > > > >> -----Original Message----- > > > > > >> From: Jorge Lopez <jorgealtxwork@xxxxxxxxx> > > > > > >> Sent: Monday, April 4, 2022 15:36 > > > > > >> To: platform-driver-x86@xxxxxxxxxxxxxxx > > > > > >> Subject: [PATCH v1 5/6] Sure Admin Security Feature > > > > > >> > > > > > >> HP Commercial PC's have several BIOS settings that control its > > > > > >> behaviour and capabilities, many of which are related to security. To > > > > > >> prevent unauthorized changes to these settings, the system can be > > > > > >> configured to use a Sure Admin cryptographic signature-based > > > > > >> authorization string that the BIOS will use to verify authorization to > > > > > >> modify the setting. Behind the scenes, Sure Admin uses Secure > > > > Platform > > > > > >> Management (SPM) and WMI > > > > > >> > > > > > >> 'settings' is a file associated with Sure Admin. BIOS settings can be > > > > > >> read or written through the Sure Admin settings file in sysfs > > > > > >> > > > > > >> /sys/devices/platform/hp-wmi/sure_admin/settings > > > > > >> > > > > > >> Expected data format to update BIOS setting > > > > > >> > > > > > >> [BIOS setting],[new value],[auth token] > > > > > >> > > > > > >> Sample settings reported data > > > > > >> > > > > > >> { > > > > > >> "Class": "HPBIOS_BIOSEnumeration", > > > > > >> "Name": "USB Storage Boot", > > > > > >> "Path": "\\Advanced\\Boot Options", > > > > > >> "IsReadOnly": 0, > > > > > >> ... > > > > > >> "Value": "Enable", > > > > > >> "Size": 2, > > > > > >> "PossibleValues": [ > > > > > >> "Disable", > > > > > >> "Enable" > > > > > >> ] > > > > > >> } > > > > > >> > > > > > > > > > > > > This sounds like it has re-invented /sys/class/firmware-attributes. > > > > > > > > > > > > Shouldn't you adopt that API? > > > > > > > > > > I fully agree. Jorge as I already indicated in our off-list > > > > > conversation when you initially started working on this > > > > > feature, we already have a standardized API for querying/changing > > > > > BIOS settings from within Linux: > > > > > > > > > > > > > I agree that changing the BIOS settings from within Linux could > > > > utilize the new methodology, I will need to look closely at the > > > > requirements before I can proceed to make the changes. > > > > Keep in mind authentication of the values is done by BIOS. No Linux > > > > process validates any data name, value, or auth token; only BIOS. All > > > > data written to the sysfs file is not validated, it is just forward to > > > > BIOS. See spm_kek_store and spm_sk_store functions. > > > > > > That's fine, and it's a safer design to have BIOS validate it. > > > > > > > One point I must make clear when updating BIOS settings. any NOT > > > > read-only BIOS settings can be changed by the application at any time. > > > > This list of settings changes from one system to another. > > > > > > Right. > > > > > > > > > > > I am in disagreement with reading the settings. hp-wmi does not read > > > > one value at a time. It reads all values exposed by BIOS. See > > > > attached sample output. > > > > > > The settings can all be read at initialization time for the driver and cached > > > then. > > > > > > > The method for how all BIOS settings are reported needs to match the > > > > method how Windows products do it. It is a requirement to start > > > > migrating customers from Windows to Linux while minimizing how BIOS > > > > data is reported. > > > > > > Because we have a standardized API in Linux for this, I think it's best to > > abstract > > > this behind a userspace application/script. If they expect to see it in the > > format you > > > showed, the userspace application can take the data from Linux and > > package it that > > > way. > > > > > > You'll have richer libraries and languages and tools to work from when > > doing this too. > > > It should make it a lot less painful. > > > > > > > > > > > I will investigate the new API and bring it to the team's attention. > > > > > > > > > > > > > > > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.k > > %2F&data=04%7C01%7CMario.Limonciello%40amd.com%7Cfbde7b4dbc > > b44386a58408da189cc91d%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C > > 0%7C637849358937251820%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLj > > AwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000& > > sdata=oZGg9YneMO6luJPU0FN8CsxfBzfh9zizgm1EM%2BLCIdM%3D&re > > served=0 > > > > > > ernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Ftorvalds%2Flinux.git% > > > > 2Ftree%2FDocumentation%2FABI%2Ftesting%2Fsysfs-class-firmware- > > > > > > attributes&data=04%7C01%7CMario.Limonciello%40amd.com%7C43a12 > > > > > > f8fea104034282708da1724a70c%7C3dd8961fe4884e608e11a82d994e183d%7C > > > > > > 0%7C0%7C637847743455947225%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiM > > > > > > C4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000 > > > > > > &sdata=QhkFIYFCRXg1seDPb3qhk5qMkNQ%2B8AQXmHuke0YrWKc%3 > > > > D&reserved=0 > > > > > > > > > > and any new code (such as this patch) which implements BIOS > > > > > setting changing MUST follow this standardized API (extending > > > > > it where necessary). > > > > > > > > > > I'm sorry but this patch is not acceptable in its current form, > > > > > it needs to be *completely rewritten* to implement: > > > > > > > > > > > > > > > > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.k > > %2F&data=04%7C01%7CMario.Limonciello%40amd.com%7Cfbde7b4dbc > > b44386a58408da189cc91d%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C > > 0%7C637849358937251820%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLj > > AwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000& > > sdata=oZGg9YneMO6luJPU0FN8CsxfBzfh9zizgm1EM%2BLCIdM%3D&re > > served=0 > > > > > > ernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Ftorvalds%2Flinux.git% > > > > 2Ftree%2FDocumentation%2FABI%2Ftesting%2Fsysfs-class-firmware- > > > > > > attributes&data=04%7C01%7CMario.Limonciello%40amd.com%7C43a12 > > > > > > f8fea104034282708da1724a70c%7C3dd8961fe4884e608e11a82d994e183d%7C > > > > > > 0%7C0%7C637847743455947225%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiM > > > > > > C4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000 > > > > > > &sdata=QhkFIYFCRXg1seDPb3qhk5qMkNQ%2B8AQXmHuke0YrWKc%3 > > > > D&reserved=0 > > > > > > > > > > See: > > > > > > > > > > > > > > > > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.k > > %2F&data=04%7C01%7CMario.Limonciello%40amd.com%7Cfbde7b4dbc > > b44386a58408da189cc91d%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C > > 0%7C637849358937251820%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLj > > AwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000& > > sdata=oZGg9YneMO6luJPU0FN8CsxfBzfh9zizgm1EM%2BLCIdM%3D&re > > served=0 > > > > > > ernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Ftorvalds%2Flinux.git% > > > > 2Ftree%2Fdrivers%2Fplatform%2Fx86%2Fdell%2Fdell-wmi- > > > > > > sysman&data=04%7C01%7CMario.Limonciello%40amd.com%7C43a12f8f > > > > > > ea104034282708da1724a70c%7C3dd8961fe4884e608e11a82d994e183d%7C0% > > > > > > 7C0%7C637847743455947225%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4 > > > > > > wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&a > > > > > > mp;sdata=z5jmH7ECYBeLcndQ2vfHaUuyE04Eaf1Lymh6BjnyJ%2Fk%3D&r > > > > eserved=0 > > > > > > > > > > > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.k > > %2F&data=04%7C01%7CMario.Limonciello%40amd.com%7Cfbde7b4dbc > > b44386a58408da189cc91d%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C > > 0%7C637849358937251820%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLj > > AwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000& > > sdata=oZGg9YneMO6luJPU0FN8CsxfBzfh9zizgm1EM%2BLCIdM%3D&re > > served=0 > > > > > > ernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Ftorvalds%2Flinux.git% > > > > 2Ftree%2Fdrivers%2Fplatform%2Fx86%2Fthink- > > > > > > lmi.c&data=04%7C01%7CMario.Limonciello%40amd.com%7C43a12f8fea > > > > > > 104034282708da1724a70c%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C > > > > > > 0%7C637847743455947225%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLj > > > > > > AwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000& > > > > > > sdata=5n6w9LEB2iWpj1cOQP6Ngz94AqP1Bu4vu8T0EdN%2FThU%3D&re > > > > served=0 > > > > > > > > > > for example code / for 2 drivers from other vendors already > > > > > implementing this. > > > > > > > > > > The same applies to the: > > > > > > > > > > "[PATCH v1 3/6] Secure Platform Management Security Feature" > > > > > > > > > > this needs to be implemented as > > > > > a /sys/class/firmware-attributes/*/authentication/ > > > > > authentication method, see for example these Lenovo specific > > > > > addition to the /sys/class/firmware-attributes/*/authentication/ > > > > > userspace API for similar functionality on Lenovo Think* devices: > > > > > > > > > > > > > > > > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.k > > %2F&data=04%7C01%7CMario.Limonciello%40amd.com%7Cfbde7b4dbc > > b44386a58408da189cc91d%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C > > 0%7C637849358937251820%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLj > > AwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000& > > sdata=oZGg9YneMO6luJPU0FN8CsxfBzfh9zizgm1EM%2BLCIdM%3D&re > > served=0 > > > > > > ernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Ftorvalds%2Flinux.git% > > > > > > 2Fcommit%2F%3Fid%3D06384573a3e8335ac6797577e545c33dbf91b490& > > > > > > data=04%7C01%7CMario.Limonciello%40amd.com%7C43a12f8fea1040342827 > > > > > > 08da1724a70c%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C63784 > > > > > > 7743455947225%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLC > > > > > > JQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=eldS > > > > > > 4H3Z%2BAyL%2FG%2FO9W9rDGC37yh5sGgtUhpKdUAoSmA%3D&reser > > > > ved=0 > > > > > > > > > > I'll merge patches 1-2 sometime this week since those are > > > > > fine and it will be good to have those "out of the way", > > > > > but the rest of the series will need to be rewritten > > > > > taken the above comments into account. > > > > > > > > v1-0003-Sure-Start-Security-Feature.patch reports the number of audit > > > > logs available and reports them when read. it does not read/write > > > > BIOS settings hence it does not fall within the same category as > > > > patches v1-0002-Secure-Platform-Management-Security-Feature.patch > > and > > > > v1-0004-Sure-Admin-Security-Feature.patch > > > > Do you agree? > > > > > > > > > > > > > > Regards, > > > > > > > > > > Hans > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >> This feature requires "Update hp_wmi_group to simplify feature > > > > > >> addition" patch. > > > > > >> > > > > > >> All changes were validated on a HP ZBook Workstation, > > > > > >> HP EliteBook x360, and HP EliteBook 850 G8 notebooks. > > > > > >> > > > > > >> Signed-off-by: Jorge Lopez <jorge.lopez2@xxxxxx> > > > > > >> > > > > > >> --- > > > > > >> Based on the latest platform-drivers-x86.git/for-next > > > > > >> --- > > > > > >> drivers/platform/x86/hp-wmi.c | 977 > > > > > >> ++++++++++++++++++++++++++++++++++ > > > > > >> 1 file changed, 977 insertions(+) > > > > > >> > > > > > >> diff --git a/drivers/platform/x86/hp-wmi.c > > b/drivers/platform/x86/hp- > > > > wmi.c > > > > > >> index 918e3eaf1b67..b72ca18b77a6 100644 > > > > > >> --- a/drivers/platform/x86/hp-wmi.c > > > > > >> +++ b/drivers/platform/x86/hp-wmi.c > > > > > >> @@ -27,6 +27,7 @@ > > > > > >> #include <linux/rfkill.h> > > > > > >> #include <linux/string.h> > > > > > >> #include <linux/dmi.h> > > > > > >> +#include <linux/nls.h> > > > > > >> > > > > > >> MODULE_AUTHOR("Matthew Garrett <mjg59@xxxxxxxxxxxxx>"); > > > > > >> MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); > > > > > >> @@ -37,8 +38,16 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9- > > BE91- > > > > > >> 3D44E2C707E4"); > > > > > >> > > > > > >> #define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387- > > > > ACCDC67EF61C" > > > > > >> #define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91- > > > > 3D44E2C707E4" > > > > > >> + > > > > > >> #define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 > > > > > >> > > > > > >> +#define HPWMI_STRING_GUID "988D08E3-68F4-4c35-AF3E- > > > > 6A1B8106F83C" > > > > > >> +#define HPWMI_INTEGER_GUID "8232DE3D-663D-4327-A8F4- > > > > > >> E293ADB9BF05" > > > > > >> +#define HPWMI_ENUMERATION_GUID "2D114B49-2DFB-4130- > > B8FE- > > > > > >> 4A3C09E75133" > > > > > >> +#define HPWMI_ORDEREDLIST_GUID "14EA9746-CE1F-4098-A0E0- > > > > > >> 7045CB4DA745" > > > > > >> +#define HPWMI_PASSWORD_GUID "322F2028-0F84-4901-988E- > > > > > >> 015176049E2D" > > > > > >> +#define HPWMI_SETBIOSSETTING_GUID "1F4C91EB-DC5C-460b- > > 951D- > > > > > >> C7CB9B4B8D5E" > > > > > >> + > > > > > >> /* DMI board names of devices that should use the omen specific > > path > > > > for > > > > > >> * thermal profiles. > > > > > >> * This was obtained by taking a look in the windows omen > > command > > > > center > > > > > >> @@ -1025,6 +1034,973 @@ static const struct attribute_group > > > > > >> sure_start_group = { > > > > > >> .attrs = sure_start_attrs, > > > > > >> }; > > > > > >> > > > > > >> + > > > > > >> +static int convert_hexstr_to_str(char **hex, int input_len, char > > **str, > > > > int > > > > > >> *len) > > > > > >> +{ > > > > > >> + int ret = 0; > > > > > >> + int new_len = 0; > > > > > >> + char tmp[] = "0x00"; > > > > > >> + char *input = *hex; > > > > > >> + char *new_str = NULL; > > > > > >> + int ch; > > > > > >> + int i; > > > > > >> + > > > > > >> + if (input_len <= 0 || hex == NULL || str == NULL || len == NULL) > > > > > >> + return -EINVAL; > > > > > >> + > > > > > >> + *len = 0; > > > > > >> + *str = NULL; > > > > > >> + > > > > > >> + new_str = kmalloc(input_len, GFP_KERNEL); > > > > > >> + if (!new_str) > > > > > >> + return -ENOMEM; > > > > > >> + > > > > > >> + for (i = 0; i < input_len; i += 5) { > > > > > >> + strncpy(tmp, input + i, strlen(tmp)); > > > > > >> + ret = kstrtoint(tmp, 16, &ch); > > > > > >> + if (ret) { > > > > > >> + new_len = 0; > > > > > >> + break; > > > > > >> + } > > > > > >> + > > > > > >> + if (ch == '\\') > > > > > >> + new_str[new_len++] = '\\'; > > > > > >> + > > > > > >> + new_str[new_len++] = ch; > > > > > >> + if (ch == '\0') > > > > > >> + break; > > > > > >> + } > > > > > >> + > > > > > >> + if (new_len) { > > > > > >> + new_str[new_len] = '\0'; > > > > > >> + *str = krealloc(new_str, (new_len + 1) * sizeof(char), > > > > > >> GFP_KERNEL); > > > > > >> + if (*str) > > > > > >> + *len = new_len; > > > > > >> + else > > > > > >> + ret = -ENOMEM; > > > > > >> + } > > > > > >> + > > > > > >> + if (ret) > > > > > >> + kfree(new_str); > > > > > >> + return ret; > > > > > >> +} > > > > > >> + > > > > > >> +/* > > > > > >> + * hp_wmi_get_setting_object() - Get an ACPI object by GUID and > > > > instance > > > > > >> + * > > > > > >> + * @guid: GUID associated with the ACPI list of managed objects > > > > > >> + * @instance: Instance index to query on the ACPI list > > > > > >> + * @obj: The output ACPI object of type ACPI_TYPE_PACKAGE > > > > > >> + * or ACPI_TYPE_BUFFER (freed by the callee) > > > > > >> + * > > > > > >> + * Returns zero on success. Otherwise,an error inherited from > > > > > >> + * wmi_query_block(). It returns a obj by parameter if > > > > > >> + * the query returned object of type buffer or package, > > > > > >> + * otherwise, a null obj is returned. > > > > > >> + * > > > > > >> + * Note: obj should be freed by the callee once it is finished > > working > > > > with it > > > > > >> + */ > > > > > >> +static int hp_wmi_get_setting_object(char *guid, int instance, > > > > > >> + union acpi_object **obj) > > > > > >> +{ > > > > > >> + struct acpi_buffer output = { ACPI_ALLOCATE_LOCAL_BUFFER, > > NULL > > > > > >> }; > > > > > >> + union acpi_object *tmp = NULL; > > > > > >> + int ret; > > > > > >> + > > > > > >> + ret = wmi_query_block(guid, instance, &output); > > > > > >> + if (ACPI_SUCCESS(ret) && output.pointer != NULL) { > > > > > >> + tmp = output.pointer; > > > > > >> + if (tmp->type == ACPI_TYPE_BUFFER || tmp->type == > > > > > >> ACPI_TYPE_PACKAGE) > > > > > >> + *obj = output.pointer; > > > > > >> + else { > > > > > >> + kfree(tmp); > > > > > >> + *obj = NULL; > > > > > >> + } > > > > > >> + } > > > > > >> + > > > > > >> + return ret; > > > > > >> +} > > > > > >> + > > > > > >> + > > > > > >> +static int get_string_from_buffer(u16 **buffer, char **str) > > > > > >> +{ > > > > > >> + u16 *ptr = *buffer; > > > > > >> + u16 ptrlen; > > > > > >> + > > > > > >> + u16 size; > > > > > >> + int i; > > > > > >> + char *output = NULL; > > > > > >> + int escape = 0; > > > > > >> + > > > > > >> + ptrlen = *(ptr++); > > > > > >> + size = ptrlen / 2; > > > > > >> + > > > > > >> + if (size == 0) > > > > > >> + goto cleanup_exit; > > > > > >> + > > > > > >> + for (i = 0; i < size; i++) > > > > > >> + if (ptr[i] == '\\') > > > > > >> + escape++; > > > > > >> + > > > > > >> + size += escape; > > > > > >> + *str = kcalloc(size + 1, sizeof(char), GFP_KERNEL); > > > > > >> + if (!*str) > > > > > >> + return -ENOMEM; > > > > > >> + > > > > > >> + output = *str; > > > > > >> + > > > > > >> + /* > > > > > >> + * convert from UTF-16 unicode to ASCII > > > > > >> + */ > > > > > >> + utf16s_to_utf8s(ptr, ptrlen, UTF16_HOST_ENDIAN, output, size); > > > > > >> + > > > > > >> + if (escape == 0) { > > > > > >> + ptr += (ptrlen / 2); > > > > > >> + goto cleanup_exit; > > > > > >> + } > > > > > >> + /* > > > > > >> + * Convert escape characters only when found > > > > > >> + */ > > > > > >> + for (i = 0; i < size; i++) { > > > > > >> + if (*ptr == '\\') > > > > > >> + output[i++] = '\\'; > > > > > >> + output[i] = *ptr; > > > > > >> + ptr++; > > > > > >> + } > > > > > >> + > > > > > >> +cleanup_exit: > > > > > >> + *buffer = ptr; > > > > > >> + return 0; > > > > > >> +} > > > > > >> + > > > > > >> +static int get_integer_from_buffer(int **buffer, int *integer) > > > > > >> +{ > > > > > >> + int *ptr = PTR_ALIGN(*buffer, 4); > > > > > >> + *integer = *(ptr++); > > > > > >> + *buffer = ptr; > > > > > >> + return 0; > > > > > >> +} > > > > > >> + > > > > > >> + > > > > > >> +// Sure Admin functions > > > > > >> +enum hp_wmi_data_type { > > > > > >> + HPWMI_STRING_TYPE, > > > > > >> + HPWMI_INTEGER_TYPE, > > > > > >> + HPWMI_ENUMERATION_TYPE, > > > > > >> + HPWMI_ORDEREDLIST_TYPE, > > > > > >> + HPWMI_PASSWORD_TYPE, > > > > > >> +}; > > > > > >> + > > > > > >> +#define HP_WMI_COMMON_ELEMENTS \ > > > > > >> + "Name", \ > > > > > >> + "Value", \ > > > > > >> + "Path", \ > > > > > >> + "IsReadOnly", \ > > > > > >> + "DisplayInUI", \ > > > > > >> + "RequiresPhysicalPresence", \ > > > > > >> + "Sequence", \ > > > > > >> + "PrerequisiteSize", \ > > > > > >> + "SecurityLevel" > > > > > >> + > > > > > >> +const char *hp_wmi_string_elements[] = { > > > > > >> + HP_WMI_COMMON_ELEMENTS, > > > > > >> + "MinLength", > > > > > >> + "MaxLength" > > > > > >> +}; > > > > > >> + > > > > > >> +const char *hp_wmi_integer_elements[] = { > > > > > >> + HP_WMI_COMMON_ELEMENTS, > > > > > >> + "LowerBound", > > > > > >> + "UpperBound", > > > > > >> + "IntValue" > > > > > >> +}; > > > > > >> + > > > > > >> +const char *hp_wmi_enumeration_elements[] = { > > > > > >> + HP_WMI_COMMON_ELEMENTS, > > > > > >> + "CurrentValue", > > > > > >> + "Size" > > > > > >> +}; > > > > > >> + > > > > > >> +const char *hp_wmi_orderedlist_elements[] = { > > > > > >> + HP_WMI_COMMON_ELEMENTS, > > > > > >> + "Size" > > > > > >> +}; > > > > > >> + > > > > > >> +const char *hp_wmi_password_elements[] = { > > > > > >> + HP_WMI_COMMON_ELEMENTS, > > > > > >> + "MinLength", > > > > > >> + "MaxLength", > > > > > >> + "Size", > > > > > >> + "SupportedEncoding", > > > > > >> + "IsSet" > > > > > >> +}; > > > > > >> + > > > > > >> +const char **hp_wmi_elements[] = { > > > > > >> + hp_wmi_string_elements, > > > > > >> + hp_wmi_integer_elements, > > > > > >> + hp_wmi_enumeration_elements, > > > > > >> + hp_wmi_orderedlist_elements, > > > > > >> + hp_wmi_password_elements > > > > > >> +}; > > > > > >> + > > > > > >> +const int hp_wmi_elements_count[] = { > > > > > >> + ARRAY_SIZE(hp_wmi_string_elements), > > > > > >> + ARRAY_SIZE(hp_wmi_integer_elements), > > > > > >> + ARRAY_SIZE(hp_wmi_enumeration_elements), > > > > > >> + ARRAY_SIZE(hp_wmi_orderedlist_elements), > > > > > >> + ARRAY_SIZE(hp_wmi_password_elements) > > > > > >> +}; > > > > > >> + > > > > > >> +const char *hp_wmi_classes[] = { > > > > > >> + "HPBIOS_BIOSString", > > > > > >> + "HPBIOS_BIOSInteger", > > > > > >> + "HPBIOS_BIOSEnumeration", > > > > > >> + "HPBIOS_BIOSOrderedList", > > > > > >> + "HPBIOS_BIOSPassword" > > > > > >> +}; > > > > > >> + > > > > > >> +static DEFINE_MUTEX(buf_mutex); > > > > > >> +static int settings_buffer_size; > > > > > >> +static int buf_alloc_size; > > > > > >> +static char *hp_bios_settings_buffer; > > > > > >> + > > > > > >> + > > > > > >> +static int append_package_elements_to_buffer(union acpi_object > > > > *obj, > > > > > >> + char *buf, int alloc_size, enum > > > > > >> hp_wmi_data_type type) > > > > > >> +{ > > > > > >> + int i; > > > > > >> + union acpi_object *pobj = NULL; > > > > > >> + char *value = NULL; > > > > > >> + int value_len; > > > > > >> + char *tmpstr = NULL; > > > > > >> + char *part_tmp = NULL; > > > > > >> + int tmp_len = 0; > > > > > >> + char *part = NULL; > > > > > >> + int status = 0; > > > > > >> + int size = 0; > > > > > >> + int buf_size; > > > > > >> + > > > > > >> + if (type >= ARRAY_SIZE(hp_wmi_classes) || !buf || !obj) > > > > > >> + return -EINVAL; > > > > > >> + > > > > > >> + if (obj->type != ACPI_TYPE_PACKAGE) > > > > > >> + return -EINVAL; > > > > > >> + > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s{\n", buf); > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t\"Class\": \"%s\",\n", > > buf, > > > > > >> hp_wmi_classes[type]); > > > > > >> + > > > > > >> + for (i = 0; i < 3; i++) { > > > > > >> + pobj = &(obj->package.elements[i]); > > > > > >> + if (pobj->type == ACPI_TYPE_STRING) { > > > > > >> + status = convert_hexstr_to_str(&pobj- > > > > > >>> string.pointer, > > > > > >> + pobj->string.length, > > > > > >> &value, &value_len); > > > > > >> + if (ACPI_FAILURE(status)) > > > > > >> + continue; > > > > > >> + /* > > > > > >> + * Skip 'Value' (HP_WMI_COMMON_ELEMENTS) > > > > > >> since > > > > > >> + * 'CurrentValue' is reported. > > > > > >> + */ > > > > > >> + if (type != HPWMI_ENUMERATION_TYPE || i != 1) > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> + "%s\t\"%s\": \"%s\",\n", > > > > > >> + buf, > > > > > >> + > > > > > >> hp_wmi_elements[type][i], value); > > > > > >> + > > > > > >> + } > > > > > >> + kfree(value); > > > > > >> + value = NULL; > > > > > >> + } > > > > > >> + > > > > > >> + for (i = 3; i < hp_wmi_elements_count[type]; i++) { > > > > > >> + pobj = &(obj->package.elements[i]); > > > > > >> + > > > > > >> + if (type == HPWMI_ENUMERATION_TYPE && > > > > > >> + i == 9 && > > > > > >> + pobj->type == ACPI_TYPE_STRING) { > > > > > >> + /* > > > > > >> + * Report "CurrentValue" as "Value" > > > > > >> + */ > > > > > >> + status = convert_hexstr_to_str(&pobj- > > > > > >>> string.pointer, > > > > > >> + pobj->string.length, > > > > > >> + &value, &value_len); > > > > > >> + if (ACPI_FAILURE(status)) > > > > > >> + continue; > > > > > >> + > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> + "%s\t\"Value\": \"%s\",\n", > > > > > >> + buf, value); > > > > > >> + kfree(value); > > > > > >> + value = NULL; > > > > > >> + > > > > > >> + } else if (type == HPWMI_PASSWORD_TYPE && > > > > > >> + i == 12 && > > > > > >> + pobj->type == ACPI_TYPE_STRING) { > > > > > >> + /* > > > > > >> + * Report list of "SupportEncoding" > > > > > >> + * > > > > > >> + * "SupportedEncoding": [ > > > > > >> + * "utf-16" > > > > > >> + * ], > > > > > >> + * > > > > > >> + */ > > > > > >> + > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t\"%s\": [\n", > > > > > >> + buf, hp_wmi_elements[type][i]); > > > > > >> + while (size--) { > > > > > >> + pobj = &(obj->package.elements[i]); > > > > > >> + status = convert_hexstr_to_str(&pobj- > > > > > >>> string.pointer, > > > > > >> + pobj- > > > > > >>> string.length, > > > > > >> + &value, > > > > > >> &value_len); > > > > > >> + if (ACPI_FAILURE(status)) > > > > > >> + continue; > > > > > >> + > > > > > >> + if (size) { > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> + "%s\t\t\"%s\",\n", > > > > > >> buf, value); > > > > > >> + i++; > > > > > >> + } else > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> + "%s\t\t\"%s\"\n", > > > > > >> buf, value); > > > > > >> + > > > > > >> + kfree(value); > > > > > >> + value = NULL; > > > > > >> + > > > > > >> + } > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t],\n", buf); > > > > > >> + continue; > > > > > >> + > > > > > >> + } else if (pobj->type == ACPI_TYPE_INTEGER) { > > > > > >> + /* > > > > > >> + * Report "PrerequisiteSize" and "Size" values > > > > > >> + * ... > > > > > >> + * "PrerequisiteSize": 1, > > > > > >> + * ... > > > > > >> + * "Size": 2, > > > > > >> + * ... > > > > > >> + */ > > > > > >> + if (i == 7) > > > > > >> + size = pobj->integer.value; > > > > > >> + else if (type == HPWMI_ORDEREDLIST_TYPE && i == > > > > > >> 9) > > > > > >> + size = pobj->integer.value; > > > > > >> + else if (type == HPWMI_ENUMERATION_TYPE && i > > > > > >> == 10) > > > > > >> + size = pobj->integer.value; > > > > > >> + else if (type == HPWMI_PASSWORD_TYPE && i == > > > > > >> 11) > > > > > >> + size = pobj->integer.value; > > > > > >> + > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t\"%s\": > > > > > >> %lld,\n", buf, > > > > > >> + hp_wmi_elements[type][i], pobj- > > > > > >>> integer.value); > > > > > >> + } > > > > > >> + } > > > > > >> + > > > > > >> + if (type == HPWMI_ENUMERATION_TYPE) { > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t\"PossibleValues\": > > > > > >> [\n", buf); > > > > > >> + for (i = 0; i < size; i++) { > > > > > >> + pobj = &(obj->package.elements[i + > > > > > >> hp_wmi_elements_count[type]]); > > > > > >> + > > > > > >> + status = convert_hexstr_to_str(&pobj- > > > > > >>> string.pointer, > > > > > >> + pobj->string.length, > > > > > >> + &value, &value_len); > > > > > >> + if (ACPI_FAILURE(status)) > > > > > >> + break; > > > > > >> + > > > > > >> + /* > > > > > >> + * Report list of "PossibleValues" of size > > > > > >> + * "Size" > > > > > >> + * ... > > > > > >> + * "Size": 2, > > > > > >> + * "PossibleValues": [ > > > > > >> + * "Disable", > > > > > >> + * "Enable"] > > > > > >> + */ > > > > > >> + if (i == (size - 1)) > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> + "%s\t\t\"%s\"\n", buf, > > > > > >> value); > > > > > >> + else > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> + "%s\t\t\"%s\",\n", buf, > > > > > >> value); > > > > > >> + kfree(value); > > > > > >> + value = NULL; > > > > > >> + } > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t],\n", buf); > > > > > >> + } > > > > > >> + > > > > > >> + if (type == HPWMI_ORDEREDLIST_TYPE) { > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t\"Elements\": [\n", > > > > > >> buf); > > > > > >> + if (size <= 0) > > > > > >> + goto finish_ordered_list; > > > > > >> + > > > > > >> + pobj = &(obj- > > > > > >>> package.elements[hp_wmi_elements_count[type]]); > > > > > >> + status = convert_hexstr_to_str(&pobj->string.pointer, > > > > > >> + pobj->string.length, &value, > > > > > >> &value_len); > > > > > >> + if (ACPI_FAILURE(status)) > > > > > >> + goto finish_ordered_list; > > > > > >> + > > > > > >> + /* > > > > > >> + * Ordered list data is stored in hex and comma separated > > > > > >> format > > > > > >> + * Convert the data and split it to show each element > > > > > >> + */ > > > > > >> + status = convert_hexstr_to_str(&value, value_len, &tmpstr, > > > > > >> &tmp_len); > > > > > >> + if (ACPI_FAILURE(status)) > > > > > >> + goto finish_ordered_list; > > > > > >> + > > > > > >> + part_tmp = tmpstr; > > > > > >> + part = strsep(&part_tmp, ","); > > > > > >> + while (part) { > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t\t\"%s\"", > > > > > >> buf, part); > > > > > >> + part = strsep(&part_tmp, ","); > > > > > >> + if (part) > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s,\n", > > > > > >> buf); > > > > > >> + else > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\n", > > > > > >> buf); > > > > > >> + } > > > > > >> + } > > > > > >> + > > > > > >> +finish_ordered_list: > > > > > >> + if (type == HPWMI_ORDEREDLIST_TYPE) > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t],\n", buf); > > > > > >> + > > > > > >> + /* > > > > > >> + * remove trailing comma > > > > > >> + */ > > > > > >> + if (buf_size > 3) > > > > > >> + buf[buf_size - 2] = ' '; > > > > > >> + > > > > > >> + kfree(tmpstr); > > > > > >> + kfree(value); > > > > > >> + return snprintf(buf, alloc_size, "%s},\n", buf); > > > > > >> +} > > > > > >> + > > > > > >> +static int append_buffer_elements_to_buffer(union acpi_object > > *obj, > > > > > >> + char *buf, int alloc_size, enum > > > > > >> hp_wmi_data_type type) > > > > > >> +{ > > > > > >> + int buf_size; > > > > > >> + int status; > > > > > >> + char *str = NULL; > > > > > >> + int i; > > > > > >> + int j; > > > > > >> + int integer; > > > > > >> + int size = 0; > > > > > >> + > > > > > >> + if (type >= ARRAY_SIZE(hp_wmi_classes) || !buf || !obj) > > > > > >> + return -EINVAL; > > > > > >> + > > > > > >> + if (obj->type != ACPI_TYPE_BUFFER) > > > > > >> + return -EINVAL; > > > > > >> + > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s{\n", buf); > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t\"Class\": \"%s\",\n", > > buf, > > > > > >> hp_wmi_classes[type]); > > > > > >> + > > > > > >> + for (i = 0; i < 3; i++) { > > > > > >> + status = get_string_from_buffer((u16 **)&obj- > > > > > >>> buffer.pointer, &str); > > > > > >> + if (ACPI_SUCCESS(status)) { > > > > > >> + /* > > > > > >> + * Skip 'Value' (HP_WMI_COMMON_ELEMENTS) > > > > > >> since > > > > > >> + * 'CurrentValue' is reported. > > > > > >> + */ > > > > > >> + if (type != HPWMI_ENUMERATION_TYPE || i != 1) > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> + "%s\t\"%s\": \"%s\",\n", > > > > > >> + buf, > > > > > >> + > > > > > >> hp_wmi_elements[type][i], str); > > > > > >> + } > > > > > >> + kfree(str); > > > > > >> + str = NULL; > > > > > >> + > > > > > >> + } > > > > > >> + > > > > > >> + for (i = 3; i < hp_wmi_elements_count[type]; i++) { > > > > > >> + if (type == HPWMI_ENUMERATION_TYPE && i == 9) { > > > > > >> + status = get_string_from_buffer((u16 **)&obj- > > > > > >>> buffer.pointer, &str); > > > > > >> + if (ACPI_SUCCESS(status)) { > > > > > >> + /* > > > > > >> + * Report "CurrentValue" as "Value" > > > > > >> + */ > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> + "%s\t\"Value\": \"%s\",\n", > > > > > >> buf, str); > > > > > >> + } > > > > > >> + kfree(str); > > > > > >> + str = NULL; > > > > > >> + continue; > > > > > >> + > > > > > >> + } else if (type == HPWMI_PASSWORD_TYPE && i == 12) { > > > > > >> + /* > > > > > >> + * Report list of "SupportEncoding" > > > > > >> + * > > > > > >> + * "SupportedEncoding": [ > > > > > >> + * "utf-16" > > > > > >> + * ], > > > > > >> + * > > > > > >> + */ > > > > > >> + > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t\"%s\": [\n", > > > > > >> + buf, hp_wmi_elements[type][i]); > > > > > >> + for (j = 0; j < size; j++) { > > > > > >> + status = get_string_from_buffer((u16 > > > > > >> **)&obj->buffer.pointer, &str); > > > > > >> + if (ACPI_SUCCESS(status)) { > > > > > >> + if (j == size - 1) > > > > > >> + buf_size = snprintf(buf, > > > > > >> alloc_size, > > > > > >> + > > > > > >> "%s\t\t\"%s\"\n", buf, str); > > > > > >> + else > > > > > >> + buf_size = snprintf(buf, > > > > > >> alloc_size, > > > > > >> + > > > > > >> "%s\t\t\"%s\",\n", buf, str); > > > > > >> + } > > > > > >> + kfree(str); > > > > > >> + str = NULL; > > > > > >> + } > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t],\n", buf); > > > > > >> + continue; > > > > > >> + } > > > > > >> + > > > > > >> + size = 0; > > > > > >> + status = get_integer_from_buffer((int **)&obj- > > > > > >>> buffer.pointer, &integer); > > > > > >> + if (ACPI_SUCCESS(status)) { > > > > > >> + /* > > > > > >> + * Report "PrerequisiteSize" and "Size" values > > > > > >> + * ... > > > > > >> + * "PrerequisiteSize": 1, > > > > > >> + * ... > > > > > >> + * "Size": 2, > > > > > >> + * ... > > > > > >> + */ > > > > > >> + if (i == 7) > > > > > >> + size = integer; > > > > > >> + else if (type == HPWMI_ENUMERATION_TYPE && i > > > > > >> == 10) > > > > > >> + size = integer; > > > > > >> + else if (type == HPWMI_ORDEREDLIST_TYPE && i == > > > > > >> 9) > > > > > >> + size = integer; > > > > > >> + else if (type == HPWMI_PASSWORD_TYPE && i == > > > > > >> 11) > > > > > >> + size = integer; > > > > > >> + > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t\"%s\": > > > > > >> %d,\n", buf, > > > > > >> + hp_wmi_elements[type][i], > > > > > >> integer); > > > > > >> + } > > > > > >> + > > > > > >> + if (size > 20) > > > > > >> + pr_warn("%s exceeded the maximum number of > > > > > >> elements supported or data may be malformed\n", > > > > > >> + hp_wmi_elements[type][i]); > > > > > >> + > > > > > >> + if (ACPI_SUCCESS(status) && i == 7) { > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> "%s\t\"Prerequisites\": [\n", buf); > > > > > >> + for (j = 0; j < size; j++) { > > > > > >> + status = get_string_from_buffer((u16 > > > > > >> **)&obj->buffer.pointer, &str); > > > > > >> + if (ACPI_SUCCESS(status)) { > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> "%s\t\t\"%s\"", buf, str); > > > > > >> + > > > > > >> + if (j == size - 1) > > > > > >> + buf_size = snprintf(buf, > > > > > >> alloc_size, "%s\n", buf); > > > > > >> + else > > > > > >> + buf_size = snprintf(buf, > > > > > >> alloc_size, "%s,\n", buf); > > > > > >> + > > > > > >> + } > > > > > >> + kfree(str); > > > > > >> + str = NULL; > > > > > >> + } > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t],\n", buf); > > > > > >> + } > > > > > >> + } > > > > > >> + > > > > > >> + if (type == HPWMI_ENUMERATION_TYPE || type == > > > > > >> HPWMI_ORDEREDLIST_TYPE) { > > > > > >> + if (type == HPWMI_ENUMERATION_TYPE) > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> "%s\t\"PossibleValues\": [\n", buf); > > > > > >> + else > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> "%s\t\"Elements\": [\n", buf); > > > > > >> + > > > > > >> + for (i = 0; i < size; i++) { > > > > > >> + status = get_string_from_buffer((u16 **)&obj- > > > > > >>> buffer.pointer, &str); > > > > > >> + if (ACPI_SUCCESS(status)) { > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> "%s\t\t\"%s\"", buf, str); > > > > > >> + > > > > > >> + if (i == size - 1) > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> "%s\n", buf); > > > > > >> + else > > > > > >> + buf_size = snprintf(buf, alloc_size, > > > > > >> "%s,\n", buf); > > > > > >> + > > > > > >> + } > > > > > >> + kfree(str); > > > > > >> + str = NULL; > > > > > >> + } > > > > > >> + buf_size = snprintf(buf, alloc_size, "%s\t],\n", buf); > > > > > >> + } > > > > > >> + > > > > > >> + /* > > > > > >> + * remove trailing comma > > > > > >> + */ > > > > > >> + if (buf_size > 3) > > > > > >> + buf[buf_size - 2] = ' '; > > > > > >> + > > > > > >> + return snprintf(buf, alloc_size, "%s},\n", buf); > > > > > >> +} > > > > > >> + > > > > > >> +static int hp_bios_settings_free_buffer(void) > > > > > >> +{ > > > > > >> + mutex_lock(&buf_mutex); > > > > > >> + kfree(hp_bios_settings_buffer); > > > > > >> + settings_buffer_size = 0; > > > > > >> + buf_alloc_size = 0; > > > > > >> + mutex_unlock(&buf_mutex); > > > > > >> + > > > > > >> + return 0; > > > > > >> +} > > > > > >> + > > > > > >> +static int hp_bios_settings_realloc_buffer(char **buf, int > > *buf_size, > > > > > >> + int *alloc_size, > > > > > >> + struct mutex *buf_mutex) > > > > > >> +{ > > > > > >> + int new_buffer_size; > > > > > >> + char *new_buf = NULL; > > > > > >> + int ret = 0; > > > > > >> + > > > > > >> + if (*buf_size + PAGE_SIZE >= *alloc_size) { > > > > > >> + new_buffer_size = buf_alloc_size + 2 * PAGE_SIZE; > > > > > >> + > > > > > >> + mutex_lock(buf_mutex); > > > > > >> + new_buf = krealloc(*buf, new_buffer_size, GFP_KERNEL); > > > > > >> + mutex_unlock(buf_mutex); > > > > > >> + if (new_buf) { > > > > > >> + mutex_lock(buf_mutex); > > > > > >> + *buf = new_buf; > > > > > >> + *alloc_size = ksize(new_buf); > > > > > >> + mutex_unlock(buf_mutex); > > > > > >> + } else { > > > > > >> + hp_bios_settings_free_buffer(); > > > > > >> + ret = -ENOMEM; > > > > > >> + } > > > > > >> + } > > > > > >> + > > > > > >> + return ret; > > > > > >> +} > > > > > >> + > > > > > >> +static int append_settings_to_buffer(char *guid, int type, char > > **buf, > > > > > >> + int *buf_size, int *alloc_size, > > > > > >> + struct mutex *buf_mutex) > > > > > >> +{ > > > > > >> + union acpi_object *obj = NULL; > > > > > >> + int ret = 0; > > > > > >> + int status = 0; > > > > > >> + int instance = 0; > > > > > >> + > > > > > >> + /* > > > > > >> + * Query all the instances until to receive a AE_BAD_PARAMETER > > > > > >> + */ > > > > > >> + do { > > > > > >> + ret = hp_wmi_get_setting_object(guid, instance++, &obj); > > > > > >> + if (ACPI_SUCCESS(ret) && obj != NULL) { > > > > > >> + status = 0; > > > > > >> + if (obj->type == ACPI_TYPE_PACKAGE) { > > > > > >> + mutex_lock(buf_mutex); > > > > > >> + status = > > > > > >> append_package_elements_to_buffer(obj, > > > > > >> + *buf, *alloc_size, > > > > > >> type); > > > > > >> + if (status > 0) > > > > > >> + *buf_size = status; > > > > > >> + mutex_unlock(buf_mutex); > > > > > >> + > > > > > >> + } else if (obj->type == ACPI_TYPE_BUFFER) { > > > > > >> + mutex_lock(buf_mutex); > > > > > >> + status = > > > > > >> append_buffer_elements_to_buffer(obj, > > > > > >> + *buf, *alloc_size, > > > > > >> type); > > > > > >> + if (status > 0) > > > > > >> + *buf_size = status; > > > > > >> + mutex_unlock(buf_mutex); > > > > > >> + > > > > > >> + } else > > > > > >> + pr_warn("The retrieved object type(%d) is > > > > > >> not supported yet\n", > > > > > >> + obj->type); > > > > > >> + > > > > > >> + ret = hp_bios_settings_realloc_buffer(buf, buf_size, > > > > > >> alloc_size, buf_mutex); > > > > > >> + } > > > > > >> + > > > > > >> + kfree(obj); > > > > > >> + obj = NULL; > > > > > >> + > > > > > >> + } while (ACPI_SUCCESS(ret)); > > > > > >> + > > > > > >> + /* > > > > > >> + * AE_BAD_PARAMETER means the loop ended by exhaustion > > > > > >> + */ > > > > > >> + if (ret == AE_BAD_PARAMETER) > > > > > >> + ret = 0; > > > > > >> + > > > > > >> + return ret; > > > > > >> +} > > > > > >> + > > > > > >> +static int hp_bios_settings_fill_buffer(void) > > > > > >> +{ > > > > > >> + int status = 0; > > > > > >> + int initial_buffer_size = 20 * PAGE_SIZE; > > > > > >> + > > > > > >> + mutex_lock(&buf_mutex); > > > > > >> + hp_bios_settings_buffer = kmalloc(initial_buffer_size, > > > > GFP_KERNEL); > > > > > >> + mutex_unlock(&buf_mutex); > > > > > >> + if (!hp_bios_settings_buffer) > > > > > >> + return -ENOMEM; > > > > > >> + > > > > > >> + mutex_lock(&buf_mutex); > > > > > >> + buf_alloc_size = ksize(hp_bios_settings_buffer); > > > > > >> + settings_buffer_size = snprintf(hp_bios_settings_buffer, > > > > > >> + buf_alloc_size, "[\n"); > > > > > >> + mutex_unlock(&buf_mutex); > > > > > >> + > > > > > >> + status = append_settings_to_buffer(HPWMI_STRING_GUID, > > > > > >> + HPWMI_STRING_TYPE, &hp_bios_settings_buffer, > > > > > >> + &settings_buffer_size, &buf_alloc_size, &buf_mutex); > > > > > >> + if (ACPI_FAILURE(status)) > > > > > >> + pr_err("error 0x%x occurred retrieving string instances\n", > > > > > >> status); > > > > > >> + > > > > > >> + status = append_settings_to_buffer(HPWMI_INTEGER_GUID, > > > > > >> + HPWMI_INTEGER_TYPE, &hp_bios_settings_buffer, > > > > > >> + &settings_buffer_size, &buf_alloc_size, &buf_mutex); > > > > > >> + if (ACPI_FAILURE(status)) > > > > > >> + pr_err("error 0x%x occurred retrieving integer instances\n", > > > > > >> status); > > > > > >> + > > > > > >> + status = > > > > append_settings_to_buffer(HPWMI_ENUMERATION_GUID, > > > > > >> + HPWMI_ENUMERATION_TYPE, &hp_bios_settings_buffer, > > > > > >> + &settings_buffer_size, &buf_alloc_size, &buf_mutex); > > > > > >> + if (ACPI_FAILURE(status)) > > > > > >> + pr_err("error 0x%x occurred retrieving enumeration > > > > > >> instances\n", status); > > > > > >> + > > > > > >> + status = > > append_settings_to_buffer(HPWMI_ORDEREDLIST_GUID, > > > > > >> + HPWMI_ORDEREDLIST_TYPE, &hp_bios_settings_buffer, > > > > > >> + &settings_buffer_size, &buf_alloc_size, &buf_mutex); > > > > > >> + if (ACPI_FAILURE(status)) > > > > > >> + pr_err("error 0x%x occurred retrieving ordered list > > > > > >> instances\n", status); > > > > > >> + > > > > > >> + status = append_settings_to_buffer(HPWMI_PASSWORD_GUID, > > > > > >> + HPWMI_PASSWORD_TYPE, &hp_bios_settings_buffer, > > > > > >> + &settings_buffer_size, &buf_alloc_size, &buf_mutex); > > > > > >> + if (ACPI_FAILURE(status)) > > > > > >> + pr_err("error 0x%x occurred retrieving password list > > > > > >> instances\n", status); > > > > > >> + > > > > > >> + mutex_lock(&buf_mutex); > > > > > >> + /* > > > > > >> + * remove trailing comma > > > > > >> + */ > > > > > >> + if (settings_buffer_size >= 3) { > > > > > >> + if (hp_bios_settings_buffer[settings_buffer_size - 2] == ',') > > > > > >> + hp_bios_settings_buffer[settings_buffer_size - 2] = ' > > > > > >> '; > > > > > >> + } > > > > > >> + settings_buffer_size = snprintf(hp_bios_settings_buffer, > > > > > >> + buf_alloc_size, "%s]\n", > > > > > >> + hp_bios_settings_buffer); > > > > > >> + mutex_unlock(&buf_mutex); > > > > > >> + > > > > > >> + return settings_buffer_size; > > > > > >> +} > > > > > >> + > > > > > >> +/* > > > > > >> + * sure_admin_settings_read - Return a formatted file with > > settings > > > > > >> + * and possible options read from BIOS > > > > > >> + * > > > > > >> + * @filp: Pointer to file of settings read from BIOS > > > > > >> + * @kobj: Pointer to a kernel object of things that show up as > > directory > > > > in > > > > > >> the sysfs filesystem. > > > > > >> + * @attr: Pointer to list of read attributes > > > > > >> + * @buf: Pointer to buffer > > > > > >> + * @off: File current offset > > > > > >> + * @count: Buffer size > > > > > >> + * > > > > > >> + * Returns the count of unicode chars read if successful, otherwise > > > > > >> + * -ENOMEM unable to allocate memory > > > > > >> + * -EINVAL buffer not allocated or too small > > > > > >> + * > > > > > >> + */ > > > > > >> +static ssize_t sure_admin_settings_read(struct file *filp, struct > > kobject > > > > > >> *kobj, > > > > > >> + struct bin_attribute *attr, char *buf, > > > > > >> loff_t off, size_t count) > > > > > >> +{ > > > > > >> + ssize_t ret; > > > > > >> + > > > > > >> + /* clear the buffer when offset is pointing to the last position */ > > > > > >> + if (off >= settings_buffer_size && settings_buffer_size > 0) { > > > > > >> + hp_bios_settings_free_buffer(); > > > > > >> + return 0; > > > > > >> + } > > > > > >> + > > > > > >> + /* clear the buffer whenever the read starts from the first > > position > > > > > >> */ > > > > > >> + if (off == 0 && settings_buffer_size > 0) > > > > > >> + hp_bios_settings_free_buffer(); > > > > > >> + > > > > > >> + if (settings_buffer_size == 0) > > > > > >> + hp_bios_settings_fill_buffer(); > > > > > >> + > > > > > >> + mutex_lock(&buf_mutex); > > > > > >> + ret = memory_read_from_buffer(buf, count, &off, > > > > > >> hp_bios_settings_buffer, > > > > > >> + settings_buffer_size); > > > > > >> + mutex_unlock(&buf_mutex); > > > > > >> + > > > > > >> + return ret; > > > > > >> +} > > > > > >> + > > > > > >> + > > > > > >> +/* > > > > > >> + * ascii_to_utf16_unicode - Convert ascii string to UTF-16 unicode > > > > > >> + * > > > > > >> + * @p: Unicode buffer address > > > > > >> + * @str: string to convert to unicode > > > > > >> + * > > > > > >> + * Returns a void pointer to the buffer containing unicode string > > > > > >> + */ > > > > > >> +static void *ascii_to_utf16_unicode(u16 *p, const u8 *str) > > > > > >> +{ > > > > > >> + int len = strlen(str); > > > > > >> + > > > > > >> + /* > > > > > >> + * Add null character when reading an empty string > > > > > >> + */ > > > > > >> + if (len == 0) { > > > > > >> + *p++ = 2; > > > > > >> + *p++ = (u8)0x00; > > > > > >> + return p; > > > > > >> + } > > > > > >> + *p++ = len * 2; > > > > > >> + utf8s_to_utf16s(str, strlen(str), UTF16_HOST_ENDIAN, p, len); > > > > > >> + p += len; > > > > > >> + > > > > > >> + return p; > > > > > >> +} > > > > > >> + > > > > > >> +/* > > > > > >> + * hp_wmi_set_bios_setting - Set setting's value in BIOS > > > > > >> + * > > > > > >> + * @input_buffer: Input buffer address > > > > > >> + * @input_size: Input buffer size > > > > > >> + * > > > > > >> + * Returns: Count of unicode characters written to BIOS if > > successful, > > > > > >> otherwise > > > > > >> + * -ENOMEM unable to allocate memory > > > > > >> + * -EINVAL buffer not allocated or too small > > > > > >> + */ > > > > > >> +static int hp_wmi_set_bios_setting(u16 *input_buffer, u32 > > input_size) > > > > > >> +{ > > > > > >> + union acpi_object *obj; > > > > > >> + struct acpi_buffer input = {input_size, input_buffer}; > > > > > >> + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; > > > > > >> + int ret = 0; > > > > > >> + > > > > > >> + ret = wmi_evaluate_method(HPWMI_SETBIOSSETTING_GUID, 0, > > 1, > > > > > >> &input, &output); > > > > > >> + > > > > > >> + obj = output.pointer; > > > > > >> + if (!obj) > > > > > >> + return -EINVAL; > > > > > >> + > > > > > >> + if (obj->type != ACPI_TYPE_INTEGER) > > > > > >> + ret = -EINVAL; > > > > > >> + > > > > > >> + ret = obj->integer.value; > > > > > >> + kfree(obj); > > > > > >> + return ret; > > > > > >> +} > > > > > >> + > > > > > >> +/* Sure Admin Functions */ > > > > > >> + > > > > > >> +#define UTF_PREFIX ((unsigned char *)"<utf-16/>") > > > > > >> +#define BEAM_PREFIX ((unsigned char > > > > > >> *)"<BEAM/>") > > > > > >> + > > > > > >> +/* > > > > > >> + * sure_admin_settings_write - Write the contents of a formatted > > file > > > > > >> + * with settings and performs the logic > > > > > >> + * to change any settings in BIOS. > > > > > >> + * > > > > > >> + * @filp: Pointer to file of settings to be written to BIOS > > > > > >> + * @kobj: Pointer to a kernel object of things that show up as > > directory > > > > in > > > > > >> the sysfs filesystem. > > > > > >> + * @attr: Pointer to list of attributes for the write operation > > > > > >> + * @buf: Pointer to buffer > > > > > >> + * @off: File current offset > > > > > >> + * @count: Buffer size > > > > > >> + * > > > > > >> + * > > > > > >> + * Returns the count of unicode characters written to BIOS if > > > > successful, > > > > > >> otherwise > > > > > >> + * -ENOMEM unable to allocate memory > > > > > >> + * -EINVAL buffer not allocated or too small > > > > > >> + * > > > > > >> + */ > > > > > >> +static ssize_t sure_admin_settings_write(struct file *filp, struct > > kobject > > > > > >> *kobj, > > > > > >> + struct bin_attribute *attr, char *buf, loff_t off, size_t > > > > > >> count) > > > > > >> +{ > > > > > >> + int status = 0; > > > > > >> + char *part = NULL; > > > > > >> + int part_len = 0; > > > > > >> + unsigned short *buffer = NULL; > > > > > >> + unsigned short *tmpstr = NULL; > > > > > >> + int buffer_size = (count + strlen(UTF_PREFIX)) * sizeof(unsigned > > > > > >> short); > > > > > >> + > > > > > >> + buffer = kmalloc(buffer_size, GFP_KERNEL); > > > > > >> + if (!buffer) > > > > > >> + return -ENOMEM; > > > > > >> + > > > > > >> + tmpstr = buffer; > > > > > >> + part = strsep(&buf, ","); > > > > > >> + if (!part) { > > > > > >> + status = -EINVAL; > > > > > >> + goto out_free; > > > > > >> + } > > > > > >> + tmpstr = ascii_to_utf16_unicode(tmpstr, part); > > > > > >> + part = strsep(&buf, ","); > > > > > >> + if (!part) { > > > > > >> + status = -EINVAL; > > > > > >> + goto out_free; > > > > > >> + } > > > > > >> + > > > > > >> + /* Add extra buffer space when encountering an empty string */ > > > > > >> + if (!strlen(part)) > > > > > >> + buffer_size += sizeof(unsigned short); > > > > > >> + tmpstr = ascii_to_utf16_unicode(tmpstr, part); > > > > > >> + part = strsep(&buf, ","); > > > > > >> + if (!part) { > > > > > >> + status = -EINVAL; > > > > > >> + goto out_free; > > > > > >> + } > > > > > >> + part_len = strlen(part) - 1; > > > > > >> + part[part_len] = '\0'; > > > > > >> + > > > > > >> + if (strncmp(part, BEAM_PREFIX, strlen(BEAM_PREFIX)) == 0) { > > > > > >> + /* > > > > > >> + * BEAM_PREFIX is append to buffer when a signature > > > > > >> + * is provided and Sure Admin is enabled in BIOS > > > > > >> + */ > > > > > >> + // BEAM_PREFIX found, convert part to unicode > > > > > >> + tmpstr = ascii_to_utf16_unicode(tmpstr, part); > > > > > >> + // decrease buffer size allocated initially for UTF_PREFIX > > > > > >> + buffer_size -= strlen(UTF_PREFIX) * sizeof(unsigned short); > > > > > >> + } else { > > > > > >> + /* > > > > > >> + * UTF-16 prefix is append to the * buffer when a BIOS > > > > > >> + * admin password is configured in BIOS > > > > > >> + */ > > > > > >> + > > > > > >> + // append UTF_PREFIX to part and then convert it to unicode > > > > > >> + part = kasprintf(GFP_KERNEL, "%s%s", UTF_PREFIX, part); > > > > > >> + if (!part) > > > > > >> + goto out_free; > > > > > >> + > > > > > >> + tmpstr = ascii_to_utf16_unicode(tmpstr, part); > > > > > >> + kfree(part); > > > > > >> + } > > > > > >> + > > > > > >> + part = strsep(&buf, ","); > > > > > >> + if (part) { > > > > > >> + status = -EINVAL; > > > > > >> + goto out_free; > > > > > >> + } > > > > > >> + status = hp_wmi_set_bios_setting(buffer, buffer_size); > > > > > >> + if (ACPI_FAILURE(status)) > > > > > >> + status = -EINVAL; > > > > > >> + > > > > > >> +out_free: > > > > > >> + kfree(buffer); > > > > > >> + if (ACPI_SUCCESS(status)) > > > > > >> + return count; > > > > > >> + return status; > > > > > >> +} > > > > > >> + > > > > > >> +HPWMI_BINATTR_RW(sure_admin, settings, 0); > > > > > >> + > > > > > >> +static struct bin_attribute *sure_admin_binattrs[] = { > > > > > >> + &sure_admin_settings, > > > > > >> + NULL, > > > > > >> +}; > > > > > >> + > > > > > >> +static const struct attribute_group sure_admin_group = { > > > > > >> + .name = "sure_admin", > > > > > >> + .bin_attrs = sure_admin_binattrs, > > > > > >> +}; > > > > > >> + > > > > > >> static DEVICE_ATTR_RO(display); > > > > > >> static DEVICE_ATTR_RO(hddtemp); > > > > > >> static DEVICE_ATTR_RW(als); > > > > > >> @@ -1050,6 +2026,7 @@ static const struct attribute_group > > > > > >> *hp_wmi_groups[] = { > > > > > >> &hp_wmi_group, > > > > > >> &spm_group, > > > > > >> &sure_start_group, > > > > > >> + &sure_admin_group, > > > > > >> NULL, > > > > > >> }; > > > > > >> > > > > > >> -- > > > > > >> 2.25.1 > > > > >